home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / mailcmd.c < prev    next >
C/C++ Source or Header  |  1997-02-24  |  167KB  |  5,999 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: mailcmd.c,v 4.395 1996/07/03 01:52:36 mikes Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. /*======================================================================
  43.      mailcmd.c
  44.      The meat and pototoes of mail processing here:
  45.        - initial command processing and dispatch
  46.        - save message
  47.        - capture address off incoming mail
  48.        - jump to specific numbered message
  49.        - open (broach) a new folder
  50.        - search message headers (where is) command
  51.   ====*/
  52.  
  53. #include "headers.h"
  54.  
  55.  
  56. /*
  57.  * Internal Prototypes
  58.  */
  59. void      cmd_delete PROTO((struct pine *, MSGNO_S *, int));
  60. void      cmd_undelete PROTO((struct pine *, MSGNO_S *, int));
  61. void      cmd_reply PROTO((struct pine *, MSGNO_S *, int));
  62. void      cmd_forward PROTO((struct pine *, MSGNO_S *, int));
  63. void      cmd_bounce PROTO((struct pine *, MSGNO_S *, int));
  64. void      cmd_print PROTO((struct pine *, MSGNO_S *, int, int));
  65. void      cmd_save PROTO((struct pine *, MSGNO_S *, int, int));
  66. void      cmd_export PROTO((struct pine *, MSGNO_S *, int, int));
  67. void      cmd_pipe PROTO((struct pine *, MSGNO_S *, int));
  68. PIPE_S     *cmd_pipe_open PROTO((char *, char **, int, gf_io_t *));
  69. void      prime_raw_text_getc PROTO((MAILSTREAM *, long));
  70. void      cmd_flag PROTO((struct pine *, MSGNO_S *, int));
  71. int      cmd_flag_prompt PROTO((struct pine *, struct flag_screen *));
  72. long      save PROTO((struct pine *, CONTEXT_S *, char *, MSGNO_S *, int));
  73. void      get_save_fldr_from_env PROTO((char *, int, ENVELOPE *,
  74.                      struct pine *, MSGNO_S *));
  75. void      saved_date PROTO((char *, char *));
  76. int      create_for_save PROTO((MAILSTREAM *, CONTEXT_S *, char *));
  77. void      flag_string PROTO((MESSAGECACHE *, long, char *));
  78. int      select_sort PROTO((struct pine *, int));
  79. void      aggregate_select PROTO((struct pine *, MSGNO_S *, int, int));
  80. int      individual_select PROTO((struct pine *, MSGNO_S *, int, int));
  81. int      select_number PROTO((MAILSTREAM *, MSGNO_S *, long));
  82. int      select_date PROTO((MAILSTREAM *, MSGNO_S *, long));
  83. int      select_text PROTO((MAILSTREAM *, MSGNO_S *, long));
  84. int      select_flagged PROTO((MAILSTREAM *, MSGNO_S *, long));
  85. int      apply_command PROTO((struct pine *, MSGNO_S *, int));
  86. long      zoom_index PROTO((struct pine *, MSGNO_S *, long *));
  87. int      unzoom_index PROTO((struct pine *, MSGNO_S *));
  88. void      jump_to PROTO((MSGNO_S *, int, int));
  89. void      search_headers PROTO((struct pine *, MAILSTREAM *, int, MSGNO_S *));
  90. char     *currentf_sequence PROTO((MAILSTREAM *, MSGNO_S *, long, long *,int));
  91. char     *selected_sequence PROTO((MAILSTREAM *, MSGNO_S *, long *));
  92. char     *build_sequence PROTO((MAILSTREAM *, MSGNO_S *, long *));
  93. void      cmd_cancelled PROTO((char *));
  94. int      any_messages PROTO((MSGNO_S *, char *, char *));
  95. int      can_set_flag PROTO((struct pine *, char *));
  96. int      bezerk_delimiter PROTO((ENVELOPE *, gf_io_t, int));
  97. char     *move_read_msgs PROTO((MAILSTREAM *, char *, char *, long));
  98. int      read_msg_prompt PROTO((long, char *));
  99. char     *move_read_incoming PROTO((MAILSTREAM *, CONTEXT_S *, char *, char **,
  100.                     char *));
  101.  
  102.  
  103. /*
  104.  * List of Select options used by apply_* functions...
  105.  */
  106. static char *sel_pmt1 = "ALTER message selection : ";
  107. static ESCKEY_S sel_opts1[] = {
  108.     {'a', 'a', "A", "unselect All"},
  109.     {'c', 'c', "C", NULL},
  110.     {'b', 'b', "B", "Broaden selctn"},
  111.     {'n', 'n', "N", "Narrow selctn"},
  112.     {'f', 'f', "F", "Flip selected"},
  113.     {-1, 0, NULL, NULL}
  114. };
  115.  
  116.  
  117. static char *sel_pmt2 = "SELECT criteria : ";
  118. static ESCKEY_S sel_opts2[] = {
  119.     {'a', 'a', "A", "select All"},
  120.     {'c', 'c', "C", "select Cur"},
  121.     {'n', 'n', "N", "Number"},
  122.     {'d', 'd', "D", "Date"},
  123.     {'t', 't', "T", "Text"},
  124.     {'s', 's', "S", "Status"},
  125.     {-1, 0, NULL, NULL}
  126. };
  127.  
  128.  
  129. static char *sel_pmt3 = "APPLY command : ";
  130. static ESCKEY_S sel_opts3[] = {
  131.     {'d', 'd',  "D", "Del"},
  132.     {'u', 'u',  "U", "Undel"},
  133.     {'r', 'r',  "R", "Reply"},
  134.     {'f', 'f',  "F", "Forward"},
  135.     {'y', 'y',  "Y", "prYnt"},
  136.     {'t', 't',  "T", "TakeAddr"},
  137.     {'s', 's',  "S", "Save"},
  138.     {'e', 'e',  "E", "Export"},
  139.     { -1,   0, NULL, NULL},
  140.     { -1,   0, NULL, NULL},
  141.     { -1,   0, NULL, NULL},
  142.     {-1, 0, NULL, NULL}
  143. };
  144.  
  145.  
  146. static char *sel_flag = 
  147.     "Select New, Deleted, Answered, or Important messages ? ";
  148. static char *sel_flag_not = 
  149.     "Select NOT New, NOT Deleted, NOT Answered or NOT Tagged msgs ? ";
  150. static ESCKEY_S sel_flag_opt[] = {
  151.     {'n', 'n', "N", "New"},
  152.     {'*', '*', "*", "Important"},
  153.     {'d', 'd', "D", "Deleted"},
  154.     {'a', 'a', "A", "Answered"},
  155.     {'!', '!', "!", "Not"},
  156.     {-1, 0, NULL, NULL}
  157. };
  158.  
  159.  
  160. static ESCKEY_S sel_date_opt[] = {
  161.     {0, 0, NULL, NULL},
  162.     {ctrl('P'), 12, "^P", "Prev Day"},
  163.     {ctrl('N'), 13, "^N", "Next Day"},
  164.     {ctrl('X'), 11, "^X", "Cur Msg"},
  165.     {ctrl('W'), 14, "^W", "Toggle When"},
  166.     {KEY_UP,    12, "", ""},
  167.     {KEY_DOWN,  13, "", ""},
  168.     {-1, 0, NULL, NULL}
  169. };
  170.  
  171.  
  172. static char *sel_text =
  173.     "Select based on To, From, Cc, or Subject fields or All message text ? ";
  174. static ESCKEY_S sel_text_opt[] = {
  175.     {'f', 'f', "F", "From"},
  176.     {'s', 's', "S", "Subject"},
  177.     {'t', 't', "T", "To"},
  178.     {'a', 'a', "A", "All Text"},
  179.     {'c', 'c', "C", "Cc"},
  180.     {-1, 0, NULL, NULL}
  181. };
  182.  
  183. static char *select_num =
  184.   "Enter comma-delimited list of numbers (dash between ranges): ";
  185.  
  186.  
  187. /*----------------------------------------------------------------------
  188.          The giant switch on the commands for index and viewing
  189.  
  190.   Input:  command  -- The command char/code
  191.           in_index -- flag indicating command is from index
  192.           orig_command -- The original command typed before pre-processing
  193.   Output: force_mailchk -- Set to tell caller to force call to new_mail().
  194.  
  195.   Result: Manifold
  196.  
  197.           Returns 1 if the message number or attachment to show changed 
  198.  ---*/
  199. int
  200. process_cmd(state, msgmap, command, in_index, orig_command, force_mailchk)
  201.     struct pine *state;
  202.     MSGNO_S     *msgmap;
  203.     int         command, in_index, orig_command;
  204.     int        *force_mailchk;
  205. {
  206.     int           question_line, a_changed, is_unread, we_cancel;
  207.     long          new_msgno, del_count, old_msgno, cur_msgno, i,
  208.           hide_count, exld_count, select_count, old_max_msgno;
  209.     char         *newfolder, prompt[80+MAXFOLDER];
  210.     CONTEXT_S    *tc;
  211. #if    defined(DOS) && !defined(_WINDOWS)
  212.     extern long coreleft();
  213. #endif
  214.  
  215.     dprint(4, (debugfile, "\n - process_cmd((%d)%c) -\n",
  216.                                                  command, (char)command));
  217.  
  218.     question_line         = -FOOTER_ROWS(state);
  219.     state->mangled_screen = 0;
  220.     state->mangled_footer = 0;
  221.     state->mangled_header = 0;
  222.     state->next_screen    = SCREEN_FUN_NULL;
  223.     cur_msgno          = mn_get_cur(msgmap);
  224.     old_msgno             = cur_msgno;
  225.     a_changed             = 0;
  226.     *force_mailchk        = 0;
  227.  
  228.     switch (command)
  229.       {
  230.           /*------------- Help --------*/
  231.         case PF1:
  232.         case OPF1:
  233.         case OOPF1:
  234.         case OOOPF1:
  235.         case '?':
  236.         case ctrl('G'):
  237.           if(state->nr_mode) {
  238.               q_status_message(SM_ORDER, 0, 3,
  239.                    "No help text currently available");
  240.               break;
  241.           }
  242.       /*
  243.        * We're not using the h_mail_view portion of this right now because
  244.        * that call is being handled in scrolltool() before it gets
  245.        * here.  Leave it in case we change how it works.
  246.        */
  247.           helper(in_index?h_mail_index : h_mail_view,
  248.          in_index?"HELP FOR FOLDER INDEX VIEW":"HELP FOR MESSAGE VIEW",
  249.          0);
  250.           dprint(2, (debugfile,"MAIL_CMD: did help command\n"));
  251.           state->mangled_screen = 1;
  252.           break;
  253.  
  254.  
  255.           /*--------- Return to main menu ------------*/
  256.         case PF3: 
  257.         case 'm':
  258.           if(state->nr_mode && command == 'm')
  259.             goto bogus;
  260.           if(state->nr_mode && command == PF3)
  261.         goto do_quit;
  262.           state->next_screen = main_menu_screen;
  263. #if    defined(DOS) && !defined(WIN32)
  264.       flush_index_cache();        /* save room on PC */
  265. #endif
  266.           dprint(2, (debugfile,"MAIL_CMD: going back to main menu\n"));
  267.           break;
  268.  
  269.  
  270.           /*------- View mail or attachment --------*/
  271.         case ctrl('M'):
  272.         case ctrl('J'):
  273.       if(!in_index){
  274.           q_status_message(SM_ORDER | SM_DING, 0, 3,
  275.                   "No default action in the Message Text screen.");
  276.           break;
  277.       }
  278.  
  279.         case PF4:
  280.         case 'v':
  281.       if(in_index) {
  282.           if(any_messages(msgmap, NULL, "to View")){
  283.           state->next_screen = mail_view_screen;
  284. #if    defined(DOS) && !defined(WIN32)
  285.           flush_index_cache();        /* save room on PC */
  286. #endif
  287.           }
  288.       }
  289.       else if(state->nr_mode)
  290.         goto bogus;
  291.       else
  292.         attachment_screen(state, msgmap);
  293.  
  294.           break;
  295.  
  296.  
  297.           /*---------- Previous message ----------*/
  298.         case PF5: 
  299.         case KEY_UP:
  300.         case 'p':
  301.         case ctrl('P'):            
  302.       if(any_messages(msgmap, NULL, NULL)){
  303.           if(cur_msgno > 1L){
  304.           mn_dec_cur(state->mail_stream, msgmap);
  305.           if(cur_msgno == mn_get_cur(msgmap))
  306.             q_status_message(SM_ORDER, 0, 2,
  307.                   "Already on first message in Zoomed Index");
  308.           else
  309.             cur_msgno = mn_get_cur(msgmap);
  310.           }
  311.           else
  312.         q_status_message(SM_ORDER, 0, 1, "Already on first message");
  313.       }
  314.  
  315.           break;
  316.  
  317.  
  318.           /*---------- Next Message ----------*/
  319.         case PF6:
  320.         case KEY_DOWN:
  321.         case 'n':
  322.         case ctrl('N'):
  323.       if(mn_get_total(msgmap) > 0L && cur_msgno < mn_get_total(msgmap)){
  324.           mn_inc_cur(state->mail_stream, msgmap);
  325.           if(cur_msgno == mn_get_cur(msgmap))
  326.         any_messages(NULL, "more", "in Zoomed Index");
  327.           else
  328.         cur_msgno = mn_get_cur(msgmap);
  329.           }
  330.       else{
  331.           prompt[0] = '\0';
  332.           if(!state->nr_mode
  333.          && (IS_NEWS(state->mail_stream)
  334.              || (state->context_current->use & CNTXT_INCMNG))){
  335.           char nextfolder[MAXPATH];
  336.  
  337.           strcpy(nextfolder, state->cur_folder);
  338.           if(next_folder(NULL, nextfolder, nextfolder,
  339.                  state->context_current, FALSE))
  340.             strcat(prompt, ".  Press TAB for next folder.");
  341.           else
  342.             strcat(prompt, ".  No more folders to TAB to.");
  343.           }
  344.  
  345.           any_messages(NULL, (mn_get_total(msgmap) > 0L) ? "more" : NULL,
  346.                prompt[0] ? prompt : NULL);
  347.  
  348.           if(!IS_NEWS(state->mail_stream))
  349.         *force_mailchk = 1;
  350.       }
  351.           break;
  352.  
  353.  
  354.           /*---------- Delete message ----------*/
  355.         case PF9: 
  356.         case 'd':
  357.         case KEY_DEL:
  358.           if(state->nr_mode) {
  359.         if(command == PF9)
  360.           goto do_forward;
  361.         else
  362.               goto bogus;
  363.       }
  364.  
  365.       cmd_delete(state, msgmap, 0);
  366.       cur_msgno = mn_get_cur(msgmap);
  367.       break;
  368.           
  369.  
  370.           /*---------- Undelete message ----------*/
  371.         case PF10:
  372.         case 'u':
  373.           if(state->nr_mode) {
  374.         if(command == PF10)
  375.           goto do_jump;
  376.         else
  377.               goto bogus;
  378.       }
  379.  
  380.       cmd_undelete(state, msgmap, 0);
  381.           break;
  382.  
  383.  
  384.           /*---------- Reply to message ----------*/
  385.         case PF11: 
  386.         case 'r':
  387.           if(state->anonymous && command == PF11) {
  388.         if(in_index)
  389.           goto do_sortindex;
  390.         else
  391.           goto do_index;
  392.       }
  393.  
  394.           if(state->nr_mode && command == PF11)
  395.         goto do_print;
  396.           if(state->nr_mode)
  397.             goto bogus;
  398.  
  399.       cmd_reply(state, msgmap, 0);
  400.       cur_msgno = mn_get_cur(msgmap);
  401.           break;
  402.  
  403.  
  404.           /*---------- Forward message ----------*/
  405.         case PF12: 
  406.         case 'f':
  407. do_forward:
  408.           if(command == PF12) {
  409.         if(state->anonymous)
  410.               goto bogus;
  411.         if(state->nr_mode)
  412.           goto do_save;
  413.       }
  414.  
  415.       cmd_forward(state, msgmap, 0);
  416.       cur_msgno = mn_get_cur(msgmap);
  417.           break;
  418.  
  419.  
  420.           /*---------- Quit pine ------------*/
  421.         case 'q':
  422.         case OPF3:
  423.           if(state->nr_mode && command == OPF3)
  424.             goto do_export;
  425. do_quit:
  426.       state->next_screen = quit_screen;
  427.       dprint(1, (debugfile,"MAIL_CMD: quit\n"));            
  428.           break;
  429.  
  430.  
  431.           /*---------- Compose message ----------*/
  432.         case OPF4:            
  433.         case 'c':
  434.           if(state->anonymous)
  435.             goto bogus;
  436.           state->prev_screen = in_index ? mail_index_screen :
  437.                                         mail_view_screen;
  438. #if    defined(DOS) && !defined(WIN32)
  439.       flush_index_cache();        /* save room on PC */
  440. #endif
  441.           compose_screen(state);
  442.           state->mangled_screen = 1;
  443.           break;
  444.  
  445.  
  446.           /*--------- Folders menu ------------*/
  447.     case OPF5: 
  448.         case 'l':
  449.           if(state->anonymous)
  450.         goto bogus;
  451.           if(state->nr_mode) {
  452.         if(command == OPF5)
  453.           goto do_sortindex;
  454.         goto bogus;
  455.       }
  456.           state->next_screen = folder_screen;
  457. #if    defined(DOS) && !defined(WIN32)
  458.       flush_index_cache();        /* save room on PC */
  459. #endif
  460.           dprint(2, (debugfile,"MAIL_CMD: going to folder/collection menu\n"));
  461.           break;
  462.  
  463.  
  464.           /*---------- Open specific new folder ----------*/
  465.         case OPF6:
  466.         case 'g':
  467.           if(state->nr_mode)
  468.             goto bogus;
  469.  
  470.       tc = (state->context_last
  471.               && !(state->context_current->type & FTYPE_BBOARD)) 
  472.                        ? state->context_last : state->context_current;
  473.  
  474.           newfolder = broach_folder(question_line, 1, &tc);
  475. #if    defined(DOS) && !defined(_WINDOWS)
  476.       if(newfolder && *newfolder == '{' && coreleft() < 20000){
  477.           q_status_message(SM_ORDER | SM_DING, 3, 3,
  478.                    "Not enough memory to open IMAP folder");
  479.           newfolder = NULL;
  480.       }
  481. #endif
  482.           if(newfolder)
  483.         visit_folder(state, newfolder, tc);
  484.  
  485.           break;
  486.           
  487.             
  488.           /*------- Go to index (or tab) ----------*/
  489.         case OPF7:
  490.         case 'i':
  491. do_index:
  492.           if(!in_index) {
  493. #if    defined(DOS) && !defined(WIN32)
  494.           flush_index_cache();        /* save room on PC */
  495. #endif
  496.               state->next_screen = mail_index_screen;
  497.           }
  498.       else {
  499.           if(command == OPF7)
  500.         goto do_tab;
  501.           else
  502.         q_status_message(SM_ORDER, 0, 3, "Already in Index");
  503.           }
  504.           break;
  505.  
  506. do_tab:
  507.           /*------- Skip to next interesting message -----------*/
  508.         case TAB :
  509.           if(mn_get_total(msgmap) > 0) {
  510.               new_msgno = next_sorted_flagged((F_UNDEL 
  511.                            | F_UNSEEN
  512.                            | ((IS_NEWS(state->mail_stream)
  513.                            && F_ON(F_FAKE_NEW_IN_NEWS,
  514.                                state))
  515.                                ? F_RECENT : 0)
  516.                            | ((F_ON(F_TAB_TO_NEW,state))
  517.                                ? 0 : F_OR_FLAG)),
  518.                           state->mail_stream,
  519.                           cur_msgno + 1L, &is_unread);
  520.               if(is_unread){
  521.                   if(new_msgno > 0){
  522.               mn_set_cur(msgmap, new_msgno);
  523.                       cur_msgno = new_msgno;
  524.                   }
  525.           }
  526.       }
  527.  
  528.       /*
  529.        * If there weren't any unread messages left, OR there
  530.        * aren't any messages at all, we may want to offer to
  531.        * go on to the next folder...
  532.        */
  533.       if(!is_unread || mn_get_total(msgmap) <= 0){
  534.           char ret = 'n';
  535.           int  in_inbox = !strucmp(state->cur_folder,state->inbox_name);
  536.  
  537.           if(!state->nr_mode && state->context_current
  538.          && (((state->context_current->use & CNTXT_NEWS)
  539.               && context_isambig(state->cur_folder))
  540.              || ((state->context_current->use & CNTXT_INCMNG)
  541.              && (in_inbox
  542.                  || folder_index(state->cur_folder,
  543.                      state->context_current->folders) >=0)))){
  544.           char         nextfolder[MAXPATH];
  545.           MAILSTREAM    *nextstream = NULL;
  546.  
  547.           strcpy(nextfolder, state->cur_folder);
  548.           while(1){
  549.               if(!(next_folder(&nextstream, nextfolder, nextfolder,
  550.                        state->context_current, TRUE))){
  551.               if(!in_inbox){
  552.                   sprintf(prompt, "No more %ss.  Return to \"%s\"",
  553.                      (state->context_current->use&CNTXT_INCMNG)
  554.                        ? "incoming folder" : "news group", 
  555.                       state->inbox_name);
  556.                   ret = want_to(prompt, 'y', 'x', NO_HELP, 0, 0);
  557.                   if(ret == 'y')
  558.                 visit_folder(state, state->inbox_name,
  559.                          state->context_current);
  560.               }
  561.               else
  562.                 q_status_message1(SM_ORDER, 0, 2, "No more %ss",
  563.                      (state->context_current->use&CNTXT_INCMNG)
  564.                         ? "incoming folder" : "news group");
  565.  
  566.               break;
  567.               }
  568.  
  569.               sprintf(prompt, "View next %s \"%s\"? ",
  570.                   (state->context_current->use&CNTXT_INCMNG)
  571.                  ? "Incoming folder" : "news group",
  572.                   nextfolder);
  573.  
  574.               /*
  575.                * When help gets added, this'll have to become
  576.                * a loop like the rest...
  577.                */
  578.               if(F_OFF(F_AUTO_OPEN_NEXT_UNREAD, state)){
  579.               static ESCKEY_S next_opt[] = {
  580.                   {'y', 'y', "Y", "Yes"},
  581.                   {'n', 'n', "N", "No"},
  582.                   {TAB, 'n', "Tab", "NextNew"},
  583.                   {-1, 0, NULL, NULL}
  584.               };
  585.  
  586.               ret = radio_buttons(prompt, -FOOTER_ROWS(state),
  587.                           next_opt, 'y', 'x', NO_HELP,
  588.                           RB_NORM);
  589.               if(ret == 'x'){
  590.                   cmd_cancelled(NULL);
  591.                   break;
  592.               }
  593.               }
  594.  
  595.               if(ret == 'y' || F_ON(F_AUTO_OPEN_NEXT_UNREAD, state)){
  596.               visit_folder(state, nextfolder,
  597.                        state->context_current);
  598.               break;
  599.               }
  600.           }
  601.  
  602.           if(nextstream)
  603.             mail_close(nextstream);
  604.           }
  605.           else
  606.         any_messages(NULL,
  607.                  (mn_get_total(msgmap) > 0L)
  608.                    ? IS_NEWS(state->mail_stream)
  609.                  ? "more undeleted"
  610.                  : "more new"
  611.                    : NULL,
  612.                  NULL);
  613.       }
  614.  
  615.           break;
  616.  
  617.  
  618.           /*------- Zoom -----------*/
  619.     case OOOPF4:
  620.         case 'z':
  621.       if(F_OFF(F_ENABLE_AGG_OPS,state) || !in_index)
  622.         goto bogus;
  623.  
  624.       /*
  625.        * Right now the way zoom is implemented is sort of silly.
  626.        * There are two per-message flags where just one and a 
  627.        * global "zoom mode" flag to suppress messags from the index
  628.        * should suffice.
  629.        */
  630.       if(any_messages(msgmap, NULL, "to Zoom on")){
  631.           if(unzoom_index(state, msgmap)){
  632.           dprint(4, (debugfile, "\n\n ---- Exiting ZOOM mode ----\n"));
  633.           q_status_message(SM_ORDER,0,2, "Index Zoom Mode is now off");
  634.           }
  635.           else if(i = zoom_index(state, msgmap, &cur_msgno)){
  636.           dprint(4, (debugfile,"\n\n ---- Entering ZOOM mode ----\n"));
  637.           q_status_message2(SM_ORDER, 0, 2,
  638.     "In Zoomed Index of %s message%s.  Use \"Z\" to restore regular Index",
  639.                     comatose(i), plural(i));
  640.           }
  641.           else
  642.         any_messages(NULL, "selected", "to Zoom on");
  643.       }
  644.  
  645.           break;
  646.  
  647.  
  648.           /*---------- Jump To ----------*/
  649.        case OOPF8:
  650.       if(!in_index)
  651.         goto do_tab;
  652.        case '0':
  653.        case '1':
  654.        case '2':
  655.        case '3':
  656.        case '4':
  657.        case '5':
  658.        case '6':
  659.        case '7':
  660.        case '8':
  661.        case '9':
  662.           if(F_OFF(F_ENABLE_JUMP,state))
  663.         goto bogus;
  664.        case 'j':
  665. do_jump:
  666.       jump_to(msgmap, question_line,
  667.           (command >= '0' && command <= '9') ? command : '\0');
  668.       cur_msgno        = mn_get_cur(msgmap);
  669.       state->mangled_footer = 1;
  670.       break;
  671.  
  672.  
  673.           /*---------- Search (where is command) ----------*/
  674.        case OPF8:
  675.        case ctrl('W'):
  676.        case 'w':        /* NOTE: whereis preempted outside Index */
  677.       search_headers(state, state->mail_stream, question_line, msgmap);
  678.       cur_msgno        = mn_get_cur(msgmap);
  679.       state->mangled_footer = 1;
  680.           break;
  681.  
  682.  
  683.           /*---------- print message on paper ----------*/
  684.         case OPF9:
  685.         case 'y':
  686. do_print:
  687.           if(state->anonymous || (state->nr_mode && command == OPF9))
  688.             goto bogus;
  689.  
  690.       if(any_messages(msgmap, NULL, "to prYnt"))
  691.         cmd_print(state, msgmap, 0, in_index);
  692.  
  693.           break;
  694.  
  695.  
  696.           /*---------- Take Address ----------*/
  697.         case OPF10:
  698.         case 't':
  699.           if(state->nr_mode)
  700.             goto bogus;
  701.  
  702.       if(any_messages(msgmap, NULL, "to Take address from"))
  703.         cmd_take_addr(state, msgmap, 0);
  704.  
  705.           break;
  706.  
  707.  
  708.           /*---------- Save Message ----------*/
  709.         case OPF11: 
  710.         case 's':
  711.           if(state->anonymous || (state->nr_mode && command == OPF11))
  712.             goto bogus;
  713. do_save:
  714.       if(any_messages(msgmap, NULL, "to Save")){
  715.           cmd_save(state, msgmap, question_line, 0);
  716.           cur_msgno = mn_get_cur(msgmap);
  717.       }
  718.  
  719.           break;
  720.  
  721.  
  722.           /*---------- Export message ----------*/
  723.         case OPF12:
  724.         case 'e':
  725.           if(state->anonymous || (state->nr_mode && command == OPF12))
  726.             goto bogus;
  727. do_export:
  728.       if(any_messages(msgmap, NULL, "to Export")){
  729.           cmd_export(state, msgmap, question_line, 0);
  730.           cur_msgno = mn_get_cur(msgmap);
  731.           state->mangled_footer = 1;
  732.       }
  733.  
  734.           break;
  735.  
  736.  
  737.           /*---------- Expunge ----------*/
  738.         case OOPF3:
  739.         case 'x':
  740.           if(state->nr_mode || !in_index)
  741.             goto bogus;
  742.  
  743.           dprint(2, (debugfile, "\n - expunge -\n"));
  744.       if(IS_NEWS(state->mail_stream) && state->mail_stream->rdonly){
  745.           del_count = count_flagged(state->mail_stream, "DELETED")
  746.                            - any_lflagged(msgmap, MN_EXLD);
  747.           if(del_count > 0L){
  748.           state->mangled_footer = 1;
  749.           sprintf(prompt, "Exclude %ld message%s from %s", del_count,
  750.               plural(del_count), pretty_fn(state->cur_folder));
  751.           if(F_ON(F_AUTO_EXPUNGE, state)
  752.              || want_to(prompt, 'y', 0, NO_HELP, 0, 0) == 'y'){
  753.               msgno_exclude(state->mail_stream, msgmap);
  754.               clear_index_cache();
  755.               state->mangled_body = 1;
  756.               state->mangled_header = 1;
  757.               q_status_message2(SM_ORDER, 0, 4,
  758.                     "%s message%s excluded",
  759.                     long2string(del_count),
  760.                     plural(del_count));
  761.           }
  762.           else
  763.             any_messages(NULL, NULL, "Excluded");
  764.           }
  765.           else
  766.         any_messages(NULL, "deleted", "to Exclude");
  767.  
  768.               break;
  769.       } else if(READONLY_FOLDER){
  770.               q_status_message(SM_ORDER, 0, 4,
  771.           "Can't expunge. Folder is read-only");
  772.               break;
  773.           }
  774.  
  775.       if((del_count = count_flagged(state->mail_stream, "DELETED")) == 0){
  776.          q_status_message(SM_ORDER, 0, 4, 
  777.         "Nothing to Expunge!  No messages marked \"Deleted\".");
  778.          *force_mailchk = 1;
  779.          break;
  780.       }
  781.  
  782.       if(F_OFF(F_AUTO_EXPUNGE,state)) {
  783.           int ret;
  784.  
  785.           sprintf(prompt, "Expunge %ld message%s from %s", del_count,
  786.               plural(del_count), pretty_fn(state->cur_folder));
  787.           state->mangled_footer = 1;
  788.           if((ret = want_to(prompt, 'y', 'x', NO_HELP, 0, 0)) == 'n'){
  789.           break;
  790.           }else if(ret == 'x') {        /* ^C */
  791.           cmd_cancelled("Expunge");
  792.           break;
  793.           }
  794.       }
  795.  
  796.       /*
  797.        * count local flags so we can maintain the total count
  798.        * without having to traipse thru the whole folder every time.
  799.        * we don't want to actually remove the flags here in case
  800.        * the expunge should fail for some reason...
  801.        */
  802.       hide_count = exld_count = select_count = 0L;
  803.       if(!any_lflagged(msgmap, MN_NONE)){
  804.           /*
  805.            * Make sure c-client elt's current
  806.            */
  807.           FETCH_ALL_FLAGS(state->mail_stream);
  808.           for(i = 1L; i <= mn_get_total(msgmap); i++){
  809.           MESSAGECACHE *mc;
  810.  
  811.           if((mc = mail_elt(state->mail_stream, mn_m2raw(msgmap, i)))
  812.              && mc->deleted){
  813.               if(get_lflag(state->mail_stream, msgmap, i, MN_HIDE))
  814.             hide_count++;
  815.  
  816.               if(get_lflag(state->mail_stream, msgmap, i, MN_EXLD))
  817.             exld_count++;
  818.  
  819.               if(get_lflag(state->mail_stream, msgmap, i, MN_SLCT))
  820.             select_count++;
  821.           }
  822.           }
  823.       }
  824.  
  825.           dprint(8,(debugfile, "Expunge max:%ld cur:%ld kill:%d\n",
  826.                     mn_get_total(msgmap), cur_msgno, del_count));
  827.  
  828.       old_max_msgno = mn_get_total(msgmap);
  829.           StartInverse();
  830.           PutLine0(0, 0, "**");            /* indicate delay */
  831.           EndInverse();
  832.       MoveCursor(state->ttyo->screen_rows -FOOTER_ROWS(state), 0);
  833.           fflush(stdout);
  834.  
  835.       we_cancel = busy_alarm(1, "Busy Expunging", NULL, 0);
  836.       ps_global->expunge_in_progress = 1;
  837.       mail_expunge(state->mail_stream);
  838.       ps_global->expunge_in_progress = 0;
  839.       if(we_cancel)
  840.         cancel_busy_alarm(0);
  841.  
  842.       dprint(2,(debugfile,"expunge complete cur:%ld max:%ld\n",
  843.             mn_get_cur(msgmap), mn_get_total(msgmap)));
  844.       /*
  845.        * This is only actually necessary if this causes the width of the
  846.        * message number field to change.  That is, it depends on the
  847.        * format the user is using as well as on the max_msgno.  Since it
  848.        * should be rare, we'll just do it whenever it happens.
  849.        * Also have to check for an increase in max_msgno on new mail.
  850.        */
  851.       if(old_max_msgno >= 1000L && mn_get_total(msgmap) < 1000L
  852.          || old_max_msgno >= 10000L && mn_get_total(msgmap) < 10000L
  853.          || old_max_msgno >= 100000L && mn_get_total(msgmap) < 100000L){
  854.           clear_index_cache();
  855.           state->mangled_body = 1;
  856.       }
  857.  
  858.           /*
  859.        * mm_exists and mm_expunge take care of updating max_msgno
  860.        * and selecting a new message should the selected get removed
  861.        */
  862.           reset_check_point();
  863.  
  864.       /*
  865.        * This sort needs to be here in case the expunge discovers
  866.        * new mail.  However, if we were zoomed and the expunge didn't
  867.        * expunge all of the non-hidden messages, then we may skip the
  868.        * sort for now.
  869.        */
  870.       if(any_lflagged(msgmap, MN_HIDE))
  871.         state->unsorted_newmail = 1;
  872.       else
  873.             sort_current_folder(1, mn_get_sort(msgmap),mn_get_revsort(msgmap));
  874.  
  875.           cur_msgno = mn_get_cur(msgmap);
  876.           StartInverse();
  877.           PutLine0(0, 0, "  ");            /* indicate delay's over */
  878.           EndInverse();
  879.           fflush(stdout);
  880.           if(state->expunge_count > 0) {
  881.               q_status_message3(SM_ORDER, 3, 3,
  882.                 "Expunged %s message%s from folder \"%s\"",
  883.                 long2string(state->expunge_count),
  884.                 plural(state->expunge_count),
  885.                 pretty_fn(state->cur_folder));
  886.               state->expunge_count = 0;
  887.  
  888.           /*
  889.            * This is kind of hokey.  We decrement each type of 
  890.            * local flag's count according to the number we counted
  891.            * before the expunge...
  892.            */
  893.           if(hide_count)
  894.         dec_lflagged(msgmap, MN_HIDE, hide_count);
  895.  
  896.           if(exld_count)
  897.         dec_lflagged(msgmap, MN_EXLD, exld_count);
  898.  
  899.           if(select_count)
  900.         dec_lflagged(msgmap, MN_SLCT, select_count);
  901.  
  902.           /*
  903.            * remember: since the ps->mail_box_changed bit got
  904.            * set, the call to new_mail() in the loops that called
  905.            * us will take care of fixing up the current msgno
  906.            * if there are selected msgs and such...
  907.            */
  908.           }
  909.       else
  910.         q_status_message1(SM_ORDER, 0, 3,
  911.                   "No messages expunged from folder \"%s\"",
  912.                   pretty_fn(state->cur_folder));
  913.  
  914.           break;
  915.  
  916.  
  917.           /*------- Unexclude -----------*/
  918.     case OOPF4:
  919.         case '&':
  920.       if(!in_index)
  921.         goto bogus;
  922.       else if(!(IS_NEWS(state->mail_stream)
  923.             && state->mail_stream->rdonly))
  924.             q_status_message(SM_ORDER, 0, 3,
  925.                  "Unexclude not available for mail folders");
  926.       else{
  927.           del_count = any_lflagged(msgmap, MN_EXLD);
  928.           if(del_count > 0L){
  929.           state->mangled_footer = 1;
  930.           sprintf(prompt, "UNexclude %ld message%s in %s", del_count,
  931.               plural(del_count), pretty_fn(state->cur_folder));
  932.           if(F_ON(F_AUTO_EXPUNGE, state)
  933.              || want_to(prompt, 'y', 0, NO_HELP, 0, 0) == 'y'){
  934.               msgno_include(state->mail_stream, msgmap);
  935.               clear_index_cache();
  936.               /*
  937.                * Have to add the excluded messages into the
  938.                * sort array.
  939.                */
  940.               sort_current_folder(1, mn_get_sort(msgmap),
  941.                       mn_get_revsort(msgmap));
  942.               state->mangled_header = 1;
  943.               q_status_message2(SM_ORDER, 0, 4,
  944.                     "%s message%s UNexcluded",
  945.                     long2string(del_count),
  946.                     plural(del_count));
  947.           }
  948.           else
  949.             any_messages(NULL, NULL, "UNexcluded");
  950.           }
  951.           else
  952.         any_messages(NULL, "excluded", "to UNexclude");
  953.       }
  954.  
  955.           break;
  956.  
  957.  
  958.           /*------- Make Selection -----------*/
  959.     case OOPF5:
  960.         case ';':
  961.       if(!in_index || F_OFF(F_ENABLE_AGG_OPS, state))
  962.         goto bogus;
  963.  
  964.       if(any_messages(msgmap, NULL, "to Select")){
  965.           aggregate_select(state, msgmap, question_line, in_index);
  966.           if(in_index && any_lflagged(msgmap, MN_SLCT) > 0L
  967.          && !any_lflagged(msgmap, MN_HIDE)
  968.          && F_ON(F_AUTO_ZOOM, state))
  969.         (void) zoom_index(state, msgmap, &cur_msgno);
  970.           else
  971.         cur_msgno = mn_get_cur(msgmap);
  972.       }
  973.         
  974.           break;
  975.  
  976.  
  977.           /*------- Toggle Current Message Selection State -----------*/
  978.     case OOOPF3:
  979.         case ':':
  980.       if(F_OFF(F_ENABLE_AGG_OPS, state))
  981.         goto bogus;
  982.  
  983.       if(any_messages(msgmap, NULL, NULL)){
  984.           if(individual_select(state, msgmap, question_line, in_index)
  985.          && cur_msgno < mn_get_total(msgmap)){
  986.           /* advance current */
  987.           mn_inc_cur(state->mail_stream, msgmap);
  988.           if(cur_msgno != mn_get_cur(msgmap))
  989.             cur_msgno = mn_get_cur(msgmap);
  990.           }
  991.           else
  992.         cur_msgno = mn_get_cur(msgmap);
  993.       }
  994.  
  995.           break;
  996.  
  997.  
  998.           /*------- Apply command -----------*/
  999.     case OOPF6:
  1000.         case 'a':
  1001.       if(!in_index || F_OFF(F_ENABLE_AGG_OPS, state))
  1002.         goto bogus;
  1003.  
  1004.       if(any_messages(msgmap, NULL, NULL)){
  1005.           if(any_lflagged(msgmap, MN_SLCT) > 0L){
  1006.           if(apply_command(state, msgmap, question_line)
  1007.              && F_ON(F_AUTO_UNZOOM, state))
  1008.             unzoom_index(state, msgmap);
  1009.           }
  1010.           else
  1011.         any_messages(NULL, NULL,
  1012.                  "to Apply command to.  Try \"Select\"");
  1013.  
  1014.           cur_msgno = mn_get_cur(msgmap);
  1015.       }
  1016.  
  1017.           break;
  1018.  
  1019.  
  1020.           /*-------- Sort command -------*/
  1021.     case OOPF7:
  1022.         case '$':
  1023.       {SortOrder oldorder;
  1024.        int oldrev;
  1025. do_sortindex:
  1026.         if(!in_index){
  1027.           if(command == '$')
  1028.                 goto bogus;
  1029.           else
  1030.             goto do_jump;
  1031.         }
  1032.  
  1033.         dprint(1, (debugfile,"MAIL_CMD: sort\n"));            
  1034.         oldorder = mn_get_sort(state->msgmap);
  1035.         oldrev = mn_get_revsort(state->msgmap);
  1036.         if(select_sort(state, question_line)){
  1037.             clear_index_cache();
  1038.             sort_current_folder(0, oldorder, oldrev);
  1039.         }
  1040.  
  1041.         state->mangled_footer = 1;
  1042.       }
  1043.           break;
  1044.  
  1045.  
  1046.           /*------- Toggle Full Headers -----------*/
  1047.     case OOPF9: 
  1048.         case 'h':
  1049.           if(F_OFF(F_ENABLE_FULL_HDR,state))
  1050.             goto bogus;
  1051.           state->full_header = !state->full_header;
  1052.           q_status_message3(SM_ORDER, 0, 3,
  1053.         "Display of full headers is now o%s.  Use %s to turn back o%s",
  1054.                 state->full_header ? "n" : "ff",
  1055.                 F_ON(F_USE_FK, state) ? "F9" : "H",
  1056.                 !state->full_header ? "n" : "ff");
  1057.           a_changed = 1;
  1058.           break;
  1059.  
  1060.  
  1061.           /*------- Bounce -----------*/
  1062.     case OOPF10:
  1063.         case 'b':
  1064.           if(F_OFF(F_ENABLE_BOUNCE,state))
  1065.             goto bogus;
  1066.  
  1067.           if(command == PF12) {
  1068.         if(state->anonymous)
  1069.               goto bogus;
  1070.         if(state->nr_mode)
  1071.           goto do_save;
  1072.       }
  1073.  
  1074.       cmd_bounce(state, msgmap, 0);
  1075.       cur_msgno = mn_get_cur(msgmap);
  1076.           break;
  1077.  
  1078.  
  1079.           /*------- Flag -----------*/
  1080.     case OOPF11:
  1081.         case '*':
  1082.       if(F_OFF(F_ENABLE_FLAG,state))
  1083.         goto bogus;
  1084.  
  1085.           dprint(4, (debugfile, "\n - flag message -\n"));
  1086.       cmd_flag(state, msgmap, 0);
  1087.       cur_msgno = mn_get_cur(msgmap);
  1088.  
  1089.           break;
  1090.  
  1091.  
  1092.           /*------- Pipe message -----------*/
  1093.     case OOPF12:
  1094.         case '|':
  1095.       if(F_ON(F_ENABLE_PIPE,state)){
  1096.           cmd_pipe(state, msgmap, 0);
  1097.           break;
  1098.       }
  1099.  
  1100.  
  1101.           /*--------- Default, unknown command ----------*/
  1102.         default:
  1103.         bogus:
  1104.       bogus_command(orig_command, F_ON(F_USE_FK,state) ? "F1" : "?");
  1105.           break;
  1106.       }
  1107.  
  1108.     return(cur_msgno != old_msgno || a_changed);
  1109. }
  1110.  
  1111.  
  1112.  
  1113. /*----------------------------------------------------------------------
  1114.    Complain about bogus input
  1115.  
  1116.   Args: ch -- input command to complain about
  1117.     help -- string indicating where to get help
  1118.  
  1119.  ----*/
  1120. void
  1121. bogus_command(cmd, help)
  1122.     int   cmd;
  1123.     char *help;
  1124. {
  1125.     if(cmd == ctrl('Q') || cmd == ctrl('S'))
  1126.       q_status_message1(SM_ASYNC, 0, 2,
  1127.  "%s char received.  Set \"preserve-start-stop\" feature in Setup/Config.",
  1128.             pretty_command(cmd));
  1129.     else if(cmd == KEY_JUNK)
  1130.       q_status_message3(SM_ORDER, 0, 2,
  1131.               "Invalid key pressed.%s%s%s",
  1132.               (help) ? " Use " : "",
  1133.               (help) ?  help   : "",
  1134.               (help) ? " for help" : "");
  1135.     else
  1136.       q_status_message4(SM_ORDER, 0, 2,
  1137.               "Command \"%s\" not defined for this screen.%s%s%s",
  1138.               pretty_command(cmd),
  1139.               (help) ? " Use " : "",
  1140.               (help) ?  help   : "",
  1141.               (help) ? " for help" : "");
  1142. }
  1143.  
  1144.  
  1145.  
  1146. /*----------------------------------------------------------------------
  1147.    Complain about command on empty folder
  1148.  
  1149.   Args: map -- msgmap 
  1150.     type -- type of message that's missing
  1151.     cmd -- string explaining command attempted
  1152.  
  1153.  ----*/
  1154. int
  1155. any_messages(map, type, cmd)
  1156.     MSGNO_S *map;
  1157.     char *type, *cmd;
  1158. {
  1159.     if(mn_get_total(map) <= 0L){
  1160.     q_status_message4(SM_ORDER, 0, 2, "No %s%smessages%s%s",
  1161.               type ? type : "",
  1162.               type ? " " : "",
  1163.               (!cmd || *cmd != '.') ? " " : "",
  1164.               cmd ? cmd : "in folder");
  1165.     return(FALSE);
  1166.     }
  1167.  
  1168.     return(TRUE);
  1169. }
  1170.  
  1171.  
  1172. /*----------------------------------------------------------------------
  1173.    test whether or not we have a valid stream to set flags on
  1174.  
  1175.   Args: state -- pine state containing vital signs
  1176.     cmd -- string explaining command attempted
  1177.  
  1178.   Result: returns 1 if we can set flags, otw 0 and complains
  1179.  
  1180.  ----*/
  1181. int
  1182. can_set_flag(state, cmd)
  1183.     struct pine *state;
  1184.     char    *cmd;
  1185. {
  1186.     if(READONLY_FOLDER || state->dead_stream){
  1187.     q_status_message2(SM_ORDER | (state->dead_stream ? SM_DING : 0), 0, 3,
  1188.               "Can't %s message.  Folder is %s.", cmd,
  1189.               (state->dead_stream) ? "closed" : "read-only");
  1190.     return(FALSE);
  1191.     }
  1192.  
  1193.     return(TRUE);
  1194. }
  1195.  
  1196.  
  1197.  
  1198. /*----------------------------------------------------------------------
  1199.    Complain about command on empty folder
  1200.  
  1201.   Args: type -- type of message that's missing
  1202.     cmd -- string explaining command attempted
  1203.  
  1204.  ----*/
  1205. void
  1206. cmd_cancelled(cmd)
  1207.     char *cmd;
  1208. {
  1209.     q_status_message1(SM_INFO, 0, 2, "%s cancelled",
  1210.               cmd ? cmd : "Command");
  1211. }
  1212.  
  1213.  
  1214.  
  1215. /*----------------------------------------------------------------------
  1216.    Execute DELETE message command
  1217.  
  1218.   Args: state --  Various satate info
  1219.         msgmap --  map of c-client to local message numbers
  1220.  
  1221.  Result: with side effect of "current" message delete flag set
  1222.  
  1223.  ----*/
  1224. void
  1225. cmd_delete(state, msgmap, agg)
  1226.      struct pine *state;
  1227.      MSGNO_S     *msgmap;
  1228.      int      agg;
  1229. {
  1230.     int           is_unread;
  1231.     long      cur_msgno, new_msgno, del_count = 0L;
  1232.     char     *sequence = NULL, prompt[128];
  1233.     MESSAGECACHE *mc;
  1234.  
  1235.     dprint(4, (debugfile, "\n - delete message -\n"));
  1236.     if(!(any_messages(msgmap, NULL, "to Delete")
  1237.      && can_set_flag(state, "delete")))
  1238.       return;
  1239.  
  1240.     cur_msgno = mn_get_cur(msgmap);
  1241.  
  1242.     if(state->io_error_on_stream) {
  1243.     state->io_error_on_stream = 0;
  1244.     mail_check(state->mail_stream); /* forces write */
  1245.     }
  1246.  
  1247.     if(agg){
  1248.     if(!(sequence = selected_sequence(state->mail_stream,
  1249.                         msgmap, &del_count)))
  1250.       return;                /* silently fail */
  1251.     else
  1252.       sprintf(prompt, "%ld message%s ", del_count, plural(del_count));
  1253.     }
  1254.     else{
  1255.     del_count = 1L;                /* deleting single message */
  1256.  
  1257.     (void)mail_fetchstructure(state->mail_stream,
  1258.                   mn_m2raw(state->msgmap, cur_msgno), NULL);
  1259.     if(!(mc = mail_elt(state->mail_stream, mn_m2raw(msgmap, cur_msgno)))){
  1260.         q_status_message1(SM_ORDER, 3, 4,
  1261.                  "Can't delete message %s. Error accessing folder",
  1262.                   long2string(cur_msgno));
  1263.         return;
  1264.     }
  1265.  
  1266.     if(cur_msgno >= mn_get_total(msgmap))
  1267.       strcpy(prompt, "Last message ");
  1268.     else
  1269.       sprintf(prompt, "Message %ld ", cur_msgno);
  1270.  
  1271.     if(!mc->deleted){
  1272.         clear_index_cache_ent(cur_msgno);
  1273.         sequence = cpystr(long2string(mn_m2raw(msgmap, cur_msgno)));
  1274.     }
  1275.     else
  1276.       strcat(prompt, "already ");
  1277.     }
  1278.  
  1279.     dprint(3,(debugfile, "Delete: msg %s%s\n",
  1280.           (sequence) ? sequence : long2string(cur_msgno),
  1281.           (sequence) ? "" : " ALREADY DELETED"));
  1282.  
  1283.     if(sequence){
  1284.     mail_setflag(state->mail_stream, sequence, "\\DELETED");
  1285.     fs_give((void **)&sequence);
  1286.     check_point_change();
  1287.     update_titlebar_status();
  1288.     }
  1289.  
  1290.     if(agg){
  1291.     sprintf(prompt, "%ld selected message%s marked for deletion",
  1292.         del_count, plural(del_count));
  1293.     }
  1294.     else{
  1295.     is_unread = 1;
  1296.     if((IS_NEWS(state->mail_stream)
  1297.         || (state->context_current->use & CNTXT_INCMNG))
  1298.        || F_ON(F_DEL_SKIPS_DEL, state))
  1299.       new_msgno = next_sorted_flagged(F_UNDEL, state->mail_stream,
  1300.                       cur_msgno + 1, &is_unread);
  1301.     if(F_OFF(F_DEL_SKIPS_DEL,state) && cur_msgno < mn_get_total(msgmap)){
  1302.         mn_inc_cur(state->mail_stream, msgmap);
  1303.         cur_msgno = mn_get_cur(msgmap);
  1304.     }
  1305.     else if(F_ON(F_DEL_SKIPS_DEL,state) && is_unread){
  1306.         /* more interesting mail, goto next msg */
  1307.         mn_set_cur(msgmap, new_msgno);
  1308.         cur_msgno = new_msgno;
  1309.     }
  1310.  
  1311.     if(prompt[0])
  1312.       strcat(prompt, "marked for deletion");
  1313.  
  1314.     if((IS_NEWS(state->mail_stream)
  1315.         || (state->context_current->use & CNTXT_INCMNG))
  1316.        && !state->nr_mode && !is_unread){
  1317.         char nextfolder[MAXPATH];
  1318.  
  1319.         if(prompt[0] == '\0')
  1320.           strcat(prompt, "Last undeleted message");
  1321.  
  1322.         strcpy(nextfolder, state->cur_folder);
  1323.         if(next_folder(NULL, nextfolder, nextfolder,
  1324.                state->context_current, FALSE))
  1325.           strcat(prompt, ".  Press TAB for next folder.");
  1326.         else
  1327.           strcat(prompt, ".  No more folders to TAB to.");
  1328.     }
  1329.     }
  1330.  
  1331.     if(prompt[0])
  1332.       q_status_message(SM_ORDER, 0, 3, prompt);
  1333.  
  1334.     mn_set_cur(msgmap, cur_msgno);
  1335. }
  1336.  
  1337.  
  1338.  
  1339. /*----------------------------------------------------------------------
  1340.    Execute UNDELETE message command
  1341.  
  1342.   Args: state --  Various satate info
  1343.         msgmap --  map of c-client to local message numbers
  1344.  
  1345.  Result: with side effect of "current" message delete flag UNset
  1346.  
  1347.  ----*/
  1348. void
  1349. cmd_undelete(state, msgmap, agg)
  1350.      struct pine *state;
  1351.      MSGNO_S     *msgmap;
  1352.      int      agg;
  1353. {
  1354.     long      cur_msgno, del_count = 0L;
  1355.     char     *sequence = NULL;
  1356.     ENVELOPE     *e;
  1357.  
  1358.     dprint(4, (debugfile, "\n - undelete -\n"));
  1359.     if(!(any_messages(msgmap, NULL, "to Undelete")
  1360.      && can_set_flag(state, "undelete")))
  1361.       return;
  1362.  
  1363.     cur_msgno = mn_get_cur(msgmap);
  1364.  
  1365.     if(agg){
  1366.     if(!(sequence = selected_sequence(state->mail_stream,
  1367.                         msgmap, &del_count)))
  1368.       return;                /* silently fail */
  1369.     }
  1370.     else{
  1371.     MESSAGECACHE *mc;
  1372.  
  1373.     del_count = 1L;                /* deleting single message */
  1374.     e  = mail_fetchstructure(state->mail_stream,mn_m2raw(msgmap,cur_msgno),
  1375.                  NULL); 
  1376.     mc = mail_elt(state->mail_stream, mn_m2raw(msgmap, cur_msgno));
  1377.     if(!e || !mc) {
  1378.         q_status_message(SM_ORDER, 3, 4,
  1379.              "Can't undelete message. Error accessing folder");
  1380.         return;
  1381.     }
  1382.     else if(mc->deleted) {
  1383.         clear_index_cache_ent(cur_msgno);
  1384.         sequence = cpystr(long2string(mn_m2raw(msgmap, cur_msgno)));
  1385.     }
  1386.     else{
  1387.         q_status_message(SM_ORDER, 0, 3, 
  1388.                  "Can't undelete a message that isn't deleted");
  1389.         return;
  1390.     }
  1391.     }
  1392.  
  1393.     dprint(3,(debugfile, "Undeleted: msg %s\n",
  1394.           (sequence) ? sequence : "already deleted"));
  1395.  
  1396.     if(sequence){
  1397.     mail_clearflag(state->mail_stream, sequence, "\\DELETED");
  1398.     fs_give((void **)&sequence);
  1399.  
  1400.     if(del_count == 1L){
  1401.         update_titlebar_status();
  1402.         q_status_message(SM_ORDER, 0, 3,
  1403.                 "Deletion mark removed, message won't be deleted");
  1404.     }
  1405.     else
  1406.       q_status_message2(SM_ORDER, 0, 3,
  1407.                 "Deletion mark removed from %s message%s",
  1408.                 comatose(del_count), plural(del_count));
  1409.  
  1410.     if(state->io_error_on_stream) {
  1411.         state->io_error_on_stream = 0;
  1412.         mail_check(state->mail_stream); /* forces write */
  1413.     }
  1414.     else
  1415.       check_point_change();
  1416.     }
  1417. }
  1418.  
  1419.  
  1420.  
  1421. /*----------------------------------------------------------------------
  1422.    Execute FLAG message command
  1423.  
  1424.   Args: state --  Various satate info
  1425.         msgmap --  map of c-client to local message numbers
  1426.  
  1427.  Result: with side effect of "current" message FLAG flag set or UNset
  1428.  
  1429.  ----*/
  1430. void
  1431. cmd_flag(state, msgmap, agg)
  1432.     struct pine *state;
  1433.     MSGNO_S     *msgmap;
  1434.     int         agg;
  1435. {
  1436.     char      *flagit, *seq, *screen_text[20], **exp, **p, *answer = NULL;
  1437.     long       unflagged, flagged;
  1438.     void     (*flagger)();
  1439.     MESSAGECACHE  *mc = NULL;
  1440.     struct       flag_table  *fp;
  1441.     struct flag_screen flag_screen;
  1442.     static char   *flag_screen_text1[] = {
  1443.     "    Set desired flags for current message below.  An 'X' means set",
  1444.     "    it, and a ' ' means to unset it.  Choose \"Exit\" when finished.",
  1445.     NULL
  1446.     };
  1447.     static char   *flag_screen_text2[] = {
  1448.     "    Set desired flags below for selected messages.  A '?' means to",
  1449.     "    leave the flag unchanged, 'X' means to set it, and a ' ' means",
  1450.     "    to unset it.  Use the \"Return\" key to toggle, and choose",
  1451.     "    \"Exit\" when finished.",
  1452.     NULL
  1453.     };
  1454.     static char   *flag_screen_boiler_plate[] = {
  1455.     "",
  1456.     "            Set        Flag Name",
  1457.     "            ---   ----------------------",
  1458.     NULL
  1459.     };
  1460.     static struct  flag_table ftbl[] = {
  1461.     /*
  1462.      * At some point when user defined flags are possible,
  1463.      * it should just be a simple matter of grabbing this
  1464.      * array from the heap and explicitly filling the
  1465.      * non-system flags in at run time...
  1466.      *  {NULL, h_flag_user, F_USER, 0, 0},
  1467.      */
  1468.     {"Important", h_flag_important, F_FLAG, 0, 0},
  1469.     {"New",      h_flag_new, F_SEEN, 0, 0},
  1470.     {"Answered",  h_flag_answered, F_ANS, 0, 0},
  1471.     {"Deleted",   h_flag_deleted, F_DEL, 0, 0},
  1472.     {NULL, NO_HELP, 0, 0, 0}
  1473.     };
  1474.  
  1475.     if(!(any_messages(msgmap, NULL, "to Flag")
  1476.      && can_set_flag(state, "flag")))
  1477.       return;
  1478.  
  1479.     if(state->io_error_on_stream) {
  1480.     state->io_error_on_stream = 0;
  1481.     mail_check(state->mail_stream); /* forces write */
  1482.     return;
  1483.     }
  1484.  
  1485.     flag_screen.flag_table  = ftbl;
  1486.     flag_screen.explanation = screen_text;
  1487.     if(agg){
  1488.     if(!pseudo_selected(msgmap))
  1489.       return;
  1490.  
  1491.     exp = flag_screen_text2;
  1492.     for(fp = ftbl; fp->name; fp++){
  1493.         fp->set = CMD_FLAG_UNKN;        /* set to unknown */
  1494.         fp->ukn = TRUE;
  1495.     }
  1496.     }
  1497.     else if(mc = mail_elt(state->mail_stream,
  1498.               mn_m2raw(msgmap, mn_get_cur(msgmap)))){
  1499.     exp = flag_screen_text1;
  1500.     for(fp = &ftbl[0]; fp->name; fp++){
  1501.         fp->ukn = 0;
  1502.         fp->set = ((fp->flag == F_SEEN && !mc->seen)
  1503.                || (fp->flag == F_DEL && mc->deleted)
  1504.                || (fp->flag == F_FLAG && mc->flagged)
  1505.                || (fp->flag == F_ANS && mc->answered))
  1506.             ? CMD_FLAG_SET : CMD_FLAG_CLEAR;
  1507.     }
  1508.     }
  1509.     else{
  1510.     q_status_message(SM_ORDER | SM_DING, 3, 4,
  1511.              "Error accessing message data");
  1512.     return;
  1513.     }
  1514.  
  1515. #ifdef _WINDOWS
  1516.     if (mswin_usedialog ()) {
  1517.     if (!os_flagmsgdialog (&ftbl[0]))
  1518.       return;
  1519.     }
  1520.     else
  1521. #endif        
  1522.     if(F_ON(F_FLAG_SCREEN_DFLT, ps_global)
  1523.        || !cmd_flag_prompt(state, &flag_screen)){
  1524.     screen_text[0] = "";
  1525.     for(p = &screen_text[1]; *exp; p++, exp++)
  1526.       *p = *exp;
  1527.  
  1528.     for(exp = flag_screen_boiler_plate; *exp; p++, exp++)
  1529.       *p = *exp;
  1530.  
  1531.     *p = NULL;
  1532.  
  1533.     flag_maintenance_screen(state, &flag_screen);
  1534.     }
  1535.  
  1536.     /* reaquire the elt pointer */
  1537.     mc = mail_elt(state->mail_stream,mn_m2raw(msgmap,mn_get_cur(msgmap)));
  1538.  
  1539.     for(fp = ftbl; fp->name; fp++){
  1540.     flagger = NULL;
  1541.     switch(fp->flag){
  1542.       case F_SEEN:
  1543.         if((!agg && fp->set != !mc->seen)
  1544.            || (agg && fp->set != CMD_FLAG_UNKN)){
  1545.         flagit = "\\SEEN";
  1546.         if(fp->set){
  1547.             flagger = mail_clearflag;
  1548.             unflagged = F_SEEN;
  1549.         }
  1550.         else{
  1551.             flagger = mail_setflag;
  1552.             unflagged = F_UNSEEN;
  1553.         }
  1554.         }
  1555.  
  1556.         break;
  1557.  
  1558.       case F_ANS:
  1559.         if((!agg && fp->set != mc->answered)
  1560.            || (agg && fp->set != CMD_FLAG_UNKN)){
  1561.         flagit = "\\ANSWERED";
  1562.         if(fp->set){
  1563.             flagger = mail_setflag;
  1564.             unflagged = F_UNANS;
  1565.         }
  1566.         else{
  1567.             flagger = mail_clearflag;
  1568.             unflagged = F_ANS;
  1569.         }
  1570.         }
  1571.  
  1572.         break;
  1573.  
  1574.       case F_DEL:
  1575.         if((!agg && fp->set != mc->deleted)
  1576.            || (agg && fp->set != CMD_FLAG_UNKN)){
  1577.         flagit = "\\DELETED";
  1578.         if(fp->set){
  1579.             flagger = mail_setflag;
  1580.             unflagged = F_UNDEL;
  1581.         }
  1582.         else{
  1583.             flagger = mail_clearflag;
  1584.             unflagged = F_DEL;
  1585.         }
  1586.         }
  1587.  
  1588.         break;
  1589.  
  1590.       case F_FLAG:
  1591.         if((!agg && fp->set != mc->flagged)
  1592.            || (agg && fp->set != CMD_FLAG_UNKN)){
  1593.         flagit = "\\FLAGGED";
  1594.         if(fp->set){
  1595.             flagger = mail_setflag;
  1596.             unflagged = F_UNFLAG;
  1597.         }
  1598.         else{
  1599.             flagger = mail_clearflag;
  1600.             unflagged = F_FLAG;
  1601.         }
  1602.         }
  1603.  
  1604.         break;
  1605.  
  1606.       default:
  1607.         break;
  1608.     }
  1609.  
  1610.     flagged = 0L;
  1611.     if(flagger && (seq = currentf_sequence(state->mail_stream, msgmap,
  1612.                            unflagged, &flagged, 1))){
  1613.         (*flagger)(state->mail_stream, seq, flagit);
  1614.         fs_give((void **)&seq);
  1615.         if(flagged){
  1616.         sprintf(tmp_20k_buf, "%slagged%s%s%s%s%s message%s%s \"%s\"",
  1617.             (fp->set) ? "F" : "Unf",
  1618.             agg ? " " : "",
  1619.             agg ? long2string(flagged) : "",
  1620.             (agg && flagged != mn_total_cur(msgmap))
  1621.               ? " (of " : "",
  1622.             (agg && flagged != mn_total_cur(msgmap))
  1623.               ? comatose(mn_total_cur(msgmap)) : "",
  1624.             (agg && flagged != mn_total_cur(msgmap))
  1625.               ? ")" : "",
  1626.             agg ? plural(flagged) : " ",
  1627.             agg ? "" : long2string(mn_get_cur(msgmap)),
  1628.             fp->name);
  1629.         q_status_message(SM_ORDER, 0, 2, answer = tmp_20k_buf);
  1630.         }
  1631.     }
  1632.     }
  1633.  
  1634.     if(!answer)
  1635.       q_status_message(SM_ORDER, 0, 2, "No status flags changed.");
  1636.  
  1637.   fini:
  1638.     if(agg)
  1639.       restore_selected(msgmap);
  1640. }
  1641.  
  1642.  
  1643.  
  1644. /*----------------------------------------------------------------------
  1645.    Offer concise status line flag prompt 
  1646.  
  1647.   Args: state --  Various satate info
  1648.         flags -- flags to offer setting
  1649.  
  1650.  Result: TRUE if flag to set specified in flags struct or FALSE otw
  1651.  
  1652.  ----*/
  1653. int
  1654. cmd_flag_prompt(state, flags)
  1655.     struct pine           *state;
  1656.     struct flag_screen *flags;
  1657. {
  1658.     int            r, setflag = 1;
  1659.     struct flag_table  *fp;
  1660.     static char *flag_text = "Flag New, Deleted, Answered, or Important ? ";
  1661.     static char *flag_text2    =
  1662.     "Flag NOT New, NOT Deleted, NOT Answered, or NOT Important ? ";
  1663.     static ESCKEY_S flag_text_opt[] = {
  1664.     {'n', 'n', "N", "New"},
  1665.     {'*', '*', "*", "Important"},
  1666.     {'d', 'd', "D", "Deleted"},
  1667.     {'a', 'a', "A", "Answered"},
  1668.     {'!', '!', "!", "Not"},
  1669.     {ctrl('T'), 10, "^T", "To Flag Details"},
  1670.     {-1, 0, NULL, NULL}
  1671.     };
  1672.  
  1673.     while(1){
  1674.     r = radio_buttons(setflag ? flag_text : flag_text2,
  1675.               -FOOTER_ROWS(state), flag_text_opt, '*', 'x',
  1676.               NO_HELP, RB_NORM);
  1677.     if(r == 'x')            /* ol'cancelrooney */
  1678.       return(TRUE);
  1679.     else if(r == 10)        /* return and goto flag screen */
  1680.       return(FALSE);
  1681.     else if(r == '!')        /* flip intention */
  1682.       setflag = !setflag;
  1683.     else
  1684.       break;
  1685.     }
  1686.  
  1687.     for(fp = flags->flag_table; fp->name; fp++)
  1688.       if((r == 'n' && fp->flag == F_SEEN)
  1689.      || (r == '*' && fp->flag == F_FLAG)
  1690.      || (r == 'd' && fp->flag == F_DEL)
  1691.      || (r == 'a' && fp->flag == F_ANS)){
  1692.       fp->set = setflag ? CMD_FLAG_SET : CMD_FLAG_CLEAR;
  1693.       break;
  1694.       }
  1695.  
  1696.     return(TRUE);
  1697. }
  1698.  
  1699.  
  1700.  
  1701. /*----------------------------------------------------------------------
  1702.    Execute REPLY message command
  1703.  
  1704.   Args: state --  Various satate info
  1705.         msgmap --  map of c-client to local message numbers
  1706.  
  1707.  Result: reply sent or not
  1708.  
  1709.  ----*/
  1710. void
  1711. cmd_reply(state, msgmap, agg)
  1712.      struct pine *state;
  1713.      MSGNO_S     *msgmap;
  1714.      int      agg;
  1715. {
  1716.     if(any_messages(msgmap, NULL, "to Reply to")){
  1717. #if    defined(DOS) && !defined(WIN32)
  1718.     flush_index_cache();        /* save room on PC */
  1719. #endif
  1720.     if(agg && !pseudo_selected(msgmap))
  1721.       return;
  1722.  
  1723.     reply(state);
  1724.  
  1725.     if(agg)
  1726.       restore_selected(msgmap);
  1727.  
  1728.     state->mangled_screen = 1;
  1729.     }
  1730. }
  1731.  
  1732.  
  1733.  
  1734. /*----------------------------------------------------------------------
  1735.    Execute FORWARD message command
  1736.  
  1737.   Args: state --  Various satate info
  1738.         msgmap --  map of c-client to local message numbers
  1739.  
  1740.  Result: selected message[s] forwarded or not
  1741.  
  1742.  ----*/
  1743. void
  1744. cmd_forward(state, msgmap, agg)
  1745.      struct pine *state;
  1746.      MSGNO_S     *msgmap;
  1747.      int      agg;
  1748. {
  1749.     if(any_messages(msgmap, NULL, "to Forward")){
  1750. #if    defined(DOS) && !defined(WIN32)
  1751.     flush_index_cache();        /* save room on PC */
  1752. #endif
  1753.     if(agg && !pseudo_selected(msgmap))
  1754.       return;
  1755.  
  1756.     forward(state);
  1757.  
  1758.     if(agg)
  1759.       restore_selected(msgmap);
  1760.  
  1761.     if(state->anonymous)
  1762.       state->mangled_footer = 1;
  1763.     else
  1764.       state->mangled_screen = 1;
  1765.     }
  1766. }
  1767.  
  1768.  
  1769.  
  1770. /*----------------------------------------------------------------------
  1771.    Execute BOUNCE message command
  1772.  
  1773.   Args: state --  Various satate info
  1774.         msgmap --  map of c-client to local message numbers
  1775.  
  1776.  Result: selected message[s] bounced or not
  1777.  
  1778.  ----*/
  1779. void
  1780. cmd_bounce(state, msgmap, agg)
  1781.      struct pine *state;
  1782.      MSGNO_S     *msgmap;
  1783.      int      agg;
  1784. {
  1785.     if(any_messages(msgmap, NULL, "to Bounce")){
  1786. #if    defined(DOS) && !defined(WIN32)
  1787.     flush_index_cache();            /* save room on PC */
  1788. #endif
  1789.     if(agg && !pseudo_selected(msgmap))
  1790.       return;
  1791.  
  1792.     bounce(state);
  1793.     if(agg)
  1794.       restore_selected(msgmap);
  1795.  
  1796.     state->mangled_footer = 1;
  1797.     }
  1798. }
  1799.  
  1800.  
  1801.  
  1802. /*----------------------------------------------------------------------
  1803.    Execute save message command: prompt for folder and call function to save
  1804.  
  1805.   Args: screen_line    --  Line on the screen to prompt on
  1806.         message        --  The MESSAGECACHE entry of message to save
  1807.  
  1808.  Result: The folder lister can be called to make selection; mangled screen set
  1809.  
  1810.    This does the prompting for the folder name to save to, possibly calling 
  1811.  up the folder display for selection of folder by user.                 
  1812.  ----*/
  1813. void
  1814. cmd_save(state, msgmap, screen_line, agg)
  1815.     struct pine *state;
  1816.     MSGNO_S     *msgmap;
  1817.     int          screen_line;
  1818.     int      agg;
  1819. {
  1820.     static char          folder[MAXFOLDER+1] = {'\0'};
  1821.     static CONTEXT_S *last_context = NULL;
  1822.     char          newfolder[MAXFOLDER+1], prompt[MAXFOLDER+80],
  1823.               nmsgs[32], *p;
  1824.     int              rc, saveable_count = 0, del = 0, we_cancel = 0;
  1825.     long          i;
  1826.     HelpType          help;
  1827.     CONTEXT_S         *cntxt = NULL, *tc;
  1828.     ESCKEY_S          ekey[7];
  1829.     ENVELOPE         *e = NULL;
  1830.  
  1831.     dprint(4, (debugfile, "\n - saving message -\n"));
  1832.  
  1833.     if(agg && !pseudo_selected(msgmap))
  1834.       return;
  1835.  
  1836.     if(mn_total_cur(msgmap) <= 1L){
  1837.     nmsgs[0] = '\0';
  1838.     e = mail_fetchstructure(state->mail_stream,
  1839.                 mn_m2raw(msgmap, mn_get_cur(msgmap)), NULL);
  1840.     if(!e) {
  1841.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  1842.                  "Can't save message.  Error accessing folder");
  1843.         restore_selected(msgmap);
  1844.         return;
  1845.     }
  1846.     }
  1847.     else
  1848.       sprintf(nmsgs, "%s msgs ", comatose(mn_total_cur(msgmap)));
  1849.  
  1850.     /* start with the default save context */
  1851.     if((cntxt = default_save_context(state->context_list)) == NULL)
  1852.        cntxt = state->context_list;
  1853.  
  1854.     if(!e || ps_global->save_msg_rule == MSG_RULE_LAST
  1855.       || ps_global->save_msg_rule == MSG_RULE_DEFLT){
  1856.     if(ps_global->save_msg_rule == MSG_RULE_LAST && last_context)
  1857.       cntxt = last_context;
  1858.     else
  1859.       strcpy(folder, ps_global->VAR_DEFAULT_SAVE_FOLDER);
  1860.     }
  1861.     else
  1862.       get_save_fldr_from_env(folder, sizeof(folder), e, state, msgmap);
  1863.  
  1864.     /* how many context's can be saved to... */
  1865.     for(tc = state->context_list; tc; tc = tc->next)
  1866.       if(!(tc->type&FTYPE_BBOARD))
  1867.         saveable_count++;
  1868.  
  1869.     /* set up extra command option keys */
  1870.     rc = 0;
  1871.     ekey[rc].ch      = ctrl('T');
  1872.     ekey[rc].rval    = 2;
  1873.     ekey[rc].name    = "^T";
  1874.     ekey[rc++].label = "To Fldrs";
  1875.  
  1876.     if(saveable_count > 1){
  1877.     ekey[rc].ch      = ctrl('P');
  1878.     ekey[rc].rval    = 10;
  1879.     ekey[rc].name    = "^P";
  1880.     ekey[rc++].label = "Prev Collection";
  1881.  
  1882.     ekey[rc].ch      = ctrl('N');
  1883.     ekey[rc].rval    = 11;
  1884.     ekey[rc].name    = "^N";
  1885.     ekey[rc++].label = "Next Collection";
  1886.     }
  1887.  
  1888.     if(F_ON(F_ENABLE_TAB_COMPLETE, ps_global)){
  1889.     ekey[rc].ch      = TAB;
  1890.     ekey[rc].rval    = 12;
  1891.     ekey[rc].name    = "TAB";
  1892.     ekey[rc++].label = "Complete";
  1893.     }
  1894.  
  1895.     if(saveable_count > 1){
  1896.     ekey[rc].ch      = KEY_UP;
  1897.     ekey[rc].rval    = 10;
  1898.     ekey[rc].name    = "";
  1899.     ekey[rc++].label = "";
  1900.  
  1901.     ekey[rc].ch      = KEY_DOWN;
  1902.     ekey[rc].rval    = 11;
  1903.     ekey[rc].name    = "";
  1904.     ekey[rc++].label = "";
  1905.     }
  1906.  
  1907.     ekey[rc].ch = -1;
  1908.  
  1909.     *newfolder = '\0';
  1910.     help = NO_HELP;
  1911.     ps_global->mangled_footer = 1;
  1912.     while(1) {
  1913.     /* only show collection number if more than one available */
  1914.     if(ps_global->context_list->next)
  1915.       sprintf(prompt, "SAVE %sto folder in <%.25s> [%s] : ",
  1916.           nmsgs, cntxt->label[0], folder);
  1917.     else
  1918.       sprintf(prompt, "SAVE %sto folder [%s] : ", nmsgs, folder);
  1919.  
  1920.         rc = optionally_enter(newfolder, screen_line, 0, MAXFOLDER, 1, 0,
  1921.                               prompt, ekey, help, 0);
  1922.  
  1923.     if(rc == -1){
  1924.         q_status_message(SM_ORDER | SM_DING, 3, 3,
  1925.                  "Error reading folder name");
  1926.         rc = 1;
  1927.         break;
  1928.     }
  1929.     else if(rc == 1){
  1930.         cmd_cancelled("Save message");
  1931.         break;
  1932.     }
  1933.     else if(rc == 2) {
  1934.         int  f_rc;                /* folder_lister return val */
  1935.         void (*redraw)() = ps_global->redrawer;
  1936.  
  1937.         push_titlebar_state();
  1938.         f_rc = folder_lister(ps_global,SaveMessage,cntxt,&cntxt,newfolder,
  1939.                  NULL, ps_global->context_list, NULL);
  1940.             ClearScreen();
  1941.         pop_titlebar_state();
  1942.         redraw_titlebar();
  1943.             if(ps_global->redrawer = redraw)    /* reset old value, and test */
  1944.               (*ps_global->redrawer)();
  1945.  
  1946.         if(f_rc == 1 && F_ON(F_SELECT_WO_CONFIRM, ps_global))
  1947.           break;
  1948.     }
  1949.     else if(rc == 3){
  1950.             help = (help == NO_HELP) ? h_oe_save : NO_HELP;
  1951.     }
  1952.     else if(rc == 10){            /* Previous collection */
  1953.         CONTEXT_S *start;
  1954.         start = cntxt;
  1955.         tc    = cntxt;
  1956.  
  1957.         while(1){
  1958.         if((tc = tc->next) == NULL)
  1959.           tc = ps_global->context_list;
  1960.  
  1961.         if(tc == start)
  1962.           break;
  1963.  
  1964.         if((tc->type&FTYPE_BBOARD) == 0)
  1965.           cntxt = tc;
  1966.         }
  1967.     }
  1968.     else if(rc == 11){            /* Next collection */
  1969.         tc = cntxt;
  1970.  
  1971.         do
  1972.           if((cntxt = cntxt->next) == NULL)
  1973.         cntxt = ps_global->context_list;
  1974.         while((cntxt->type&FTYPE_BBOARD) && cntxt != tc);
  1975.     }
  1976.     else if(rc == 12){            /* file name completion! */
  1977.         if(!folder_complete(cntxt, newfolder))
  1978.           Writechar(BELL, 0);
  1979.  
  1980.     }
  1981.     else if(rc != 4)
  1982.           break;
  1983.     }
  1984.  
  1985.     dprint(9, (debugfile, "rc = %d, \"%s\"  \"%s\"\n", rc, newfolder,folder));
  1986.     if(!(rc == 1 || (!*newfolder && !*folder) || (rc == 2 && !*newfolder))) {
  1987.     removing_trailing_white_space(newfolder);
  1988.     removing_leading_white_space(newfolder);
  1989.  
  1990.     last_context = cntxt;        /* remember for next time */
  1991.     if(!*newfolder)
  1992.       strcpy(newfolder, folder);
  1993.     else
  1994.       strcpy(folder, newfolder);
  1995.  
  1996.     /* is it a nickname?  If so, copy real name to newfolder */
  1997.     if(context_isambig(newfolder)
  1998.        && (p = folder_is_nick(newfolder, cntxt->folders)))
  1999.       strcpy(newfolder, p);
  2000.  
  2001.     del = !READONLY_FOLDER && F_OFF(F_SAVE_WONT_DELETE, ps_global);
  2002.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  2003.     i = save(state, cntxt, newfolder, msgmap, del);
  2004.     if(we_cancel)
  2005.       cancel_busy_alarm(0);
  2006.  
  2007.     if(i == mn_total_cur(msgmap)){
  2008.         if(mn_total_cur(msgmap) <= 1L){
  2009.         if(ps_global->context_list->next && context_isambig(newfolder))
  2010.           sprintf(tmp_20k_buf, 
  2011.               "Message %s copied to \"%.15s%s\" in <%.15s%s>",
  2012.               long2string(mn_get_cur(msgmap)), newfolder,
  2013.               (strlen(newfolder) > 15) ? "..." : "",
  2014.               cntxt->label[0],
  2015.               (strlen(cntxt->label[0]) > 15) ? "..." : "");
  2016.         else
  2017.           sprintf(tmp_20k_buf,
  2018.               "Message %s copied to folder \"%.27s%s\"",
  2019.               long2string(mn_get_cur(msgmap)), newfolder,
  2020.               (strlen(newfolder) > 27) ? "..." : "");
  2021.         }
  2022.         else
  2023.           sprintf(tmp_20k_buf, "%s messages saved",
  2024.               comatose(mn_total_cur(msgmap)));
  2025.  
  2026.         if(del)
  2027.           strcat(tmp_20k_buf, " and deleted");
  2028.  
  2029.         q_status_message(SM_ORDER, 0, 3, tmp_20k_buf);
  2030.  
  2031.         if(!agg && F_ON(F_SAVE_ADVANCES, state)){
  2032.         mn_inc_cur(state->mail_stream, msgmap);
  2033.         }
  2034.     }
  2035.     }
  2036.     else
  2037.       cmd_cancelled("No folder named; save message");
  2038.  
  2039.     if(agg)                    /* straighten out fakes */
  2040.       restore_selected(msgmap);
  2041.  
  2042.     if(del)
  2043.       update_titlebar_status();            /* make sure they see change */
  2044. }
  2045.  
  2046.  
  2047. /*----------------------------------------------------------------------
  2048.    Grope through envelope to find default folder name to save to
  2049.  
  2050.   Args: fbuf   --  Buffer to return result in
  2051.         nfbuf  --  Size of fbuf
  2052.         e      --  The envelope to look in
  2053.         state  --  Usual pine state
  2054.         msgmap --  Message map of currently selected messages
  2055.  
  2056.  Result: The appropriate default folder name is copied into fbuf.
  2057.  ----*/
  2058. void
  2059. get_save_fldr_from_env(fbuf, nfbuf, e, state, msgmap)
  2060.     char         fbuf[];
  2061.     int          nfbuf;
  2062.     ENVELOPE    *e;
  2063.     struct pine *state;
  2064.     MSGNO_S     *msgmap;
  2065. {
  2066.     char     fakedomain[2];
  2067.     ADDRESS *tmp_adr = NULL;
  2068.     char     buf[max(MAXFOLDER,MAX_NICKNAME) + 1];
  2069.     char    *bufp;
  2070.     char    *folder_name;
  2071.     static char botch[] = "programmer botch, unknown message save rule";
  2072.     unsigned save_msg_rule;
  2073.  
  2074.     if(!e)
  2075.       return;
  2076.  
  2077.     /* copy this because we might change it below */
  2078.     save_msg_rule = state->save_msg_rule;
  2079.  
  2080.     /* first get the relevant address to base the folder name on */
  2081.     switch(save_msg_rule){
  2082.       case MSG_RULE_FROM:
  2083.       case MSG_RULE_NICK_FROM:
  2084.       case MSG_RULE_FCC_FROM:
  2085.       case MSG_RULE_FCC_FROM_DEF:
  2086.       case MSG_RULE_NICK_FROM_DEF:
  2087.         tmp_adr = e->from ? e->from : e->sender ? e->sender : NULL;
  2088.     break;
  2089.  
  2090.       case MSG_RULE_SENDER:
  2091.       case MSG_RULE_NICK_SENDER:
  2092.       case MSG_RULE_FCC_SENDER:
  2093.       case MSG_RULE_NICK_SENDER_DEF:
  2094.       case MSG_RULE_FCC_SENDER_DEF:
  2095.         tmp_adr = e->sender ? e->sender : e->from ? e->from : NULL;
  2096.     break;
  2097.  
  2098.       case MSG_RULE_RECIP:
  2099.       case MSG_RULE_NICK_RECIP:
  2100.       case MSG_RULE_FCC_RECIP:
  2101.       case MSG_RULE_NICK_RECIP_DEF:
  2102.       case MSG_RULE_FCC_RECIP_DEF:
  2103.     /* news */
  2104.     if(state->mail_stream
  2105.         && state->mail_stream->mailbox[0] == '*'){ /* IS_NEWS ? */
  2106.         char *tmp_a_string, *ng_name;
  2107.  
  2108.         fakedomain[0] = '@';
  2109.         fakedomain[1] = '\0';
  2110.  
  2111.         /* find the news group name */
  2112.         ng_name = (state->mail_stream->mailbox[1] == '{')
  2113.                ? strchr(state->mail_stream->mailbox, '}') + 1
  2114.                : &state->mail_stream->mailbox[1];
  2115.         /* copy this string so rfc822_parse_adrlist can't blast it */
  2116.         tmp_a_string = cpystr(ng_name);
  2117.         /* make an adr */
  2118.         rfc822_parse_adrlist(&tmp_adr, tmp_a_string, fakedomain);
  2119.         fs_give((void **)&tmp_a_string);
  2120.         if(tmp_adr && tmp_adr->host && tmp_adr->host[0] == '@')
  2121.           tmp_adr->host[0] = '\0';
  2122.     }
  2123.     /* not news */
  2124.     else{
  2125.         static char *fields[] = {"Resent-To", NULL};
  2126.         char *extras, *values[sizeof(fields)/sizeof(fields[0])];
  2127.  
  2128.         extras = xmail_fetchheader_lines(state->mail_stream,
  2129.                       mn_m2raw(msgmap, mn_get_cur(msgmap)),
  2130.                       fields);
  2131.         if(extras){
  2132.         long i;
  2133.  
  2134.         memset(values, 0, sizeof(fields));
  2135.         simple_header_parse(extras, fields, values);
  2136.         fs_give((void **)&extras);
  2137.  
  2138.         for(i = 0; i < sizeof(fields)/sizeof(fields[0]); i++)
  2139.           if(values[i]){
  2140.               if(tmp_adr)        /* take last matching value */
  2141.             mail_free_address(&tmp_adr);
  2142.  
  2143.               /* build a temporary address list */
  2144.               fakedomain[0] = '@';
  2145.               fakedomain[1] = '\0';
  2146.               rfc822_parse_adrlist(&tmp_adr, values[i], fakedomain);
  2147.               fs_give((void **)&values[i]);
  2148.           }
  2149.         }
  2150.  
  2151.         if(!tmp_adr)
  2152.           tmp_adr = e->to ? e->to : NULL;
  2153.     }
  2154.  
  2155.     break;
  2156.       
  2157.       default:
  2158.     panic(botch);
  2159.     break;
  2160.     }
  2161.  
  2162.     /* For that address, lookup the fcc or nickname from address book */
  2163.     switch(save_msg_rule){
  2164.       case MSG_RULE_NICK_FROM:
  2165.       case MSG_RULE_NICK_SENDER:
  2166.       case MSG_RULE_NICK_RECIP:
  2167.       case MSG_RULE_FCC_FROM:
  2168.       case MSG_RULE_FCC_SENDER:
  2169.       case MSG_RULE_FCC_RECIP:
  2170.       case MSG_RULE_NICK_FROM_DEF:
  2171.       case MSG_RULE_NICK_SENDER_DEF:
  2172.       case MSG_RULE_NICK_RECIP_DEF:
  2173.       case MSG_RULE_FCC_FROM_DEF:
  2174.       case MSG_RULE_FCC_SENDER_DEF:
  2175.       case MSG_RULE_FCC_RECIP_DEF:
  2176.     switch(save_msg_rule){
  2177.       case MSG_RULE_NICK_FROM:
  2178.       case MSG_RULE_NICK_SENDER:
  2179.       case MSG_RULE_NICK_RECIP:
  2180.       case MSG_RULE_NICK_FROM_DEF:
  2181.       case MSG_RULE_NICK_SENDER_DEF:
  2182.       case MSG_RULE_NICK_RECIP_DEF:
  2183.         bufp = get_nickname_from_addr(tmp_adr, buf);
  2184.         break;
  2185.  
  2186.       case MSG_RULE_FCC_FROM:
  2187.       case MSG_RULE_FCC_SENDER:
  2188.       case MSG_RULE_FCC_RECIP:
  2189.       case MSG_RULE_FCC_FROM_DEF:
  2190.       case MSG_RULE_FCC_SENDER_DEF:
  2191.       case MSG_RULE_FCC_RECIP_DEF:
  2192.         bufp = get_fcc_from_addr(tmp_adr, buf);
  2193.         break;
  2194.     }
  2195.  
  2196.     if(bufp && *bufp){
  2197.         fbuf[nfbuf - 1] = '\0';
  2198.         strncpy(fbuf, bufp, nfbuf - 1);
  2199.     }
  2200.     else
  2201.       /* fall back to non-nick/non-fcc version of rule */
  2202.       switch(save_msg_rule){
  2203.         case MSG_RULE_NICK_FROM:
  2204.         case MSG_RULE_FCC_FROM:
  2205.           save_msg_rule = MSG_RULE_FROM;
  2206.           break;
  2207.  
  2208.         case MSG_RULE_NICK_SENDER:
  2209.         case MSG_RULE_FCC_SENDER:
  2210.           save_msg_rule = MSG_RULE_SENDER;
  2211.           break;
  2212.  
  2213.         case MSG_RULE_NICK_RECIP:
  2214.         case MSG_RULE_FCC_RECIP:
  2215.           save_msg_rule = MSG_RULE_RECIP;
  2216.           break;
  2217.         
  2218.         default:
  2219.           strcpy(fbuf, ps_global->VAR_DEFAULT_SAVE_FOLDER);
  2220.           break;
  2221.       }
  2222.  
  2223.     break;
  2224.     }
  2225.  
  2226.     /* get the username out of the mailbox for this address */
  2227.     switch(save_msg_rule){
  2228.       case MSG_RULE_FROM:
  2229.       case MSG_RULE_SENDER:
  2230.       case MSG_RULE_RECIP:
  2231.     /*
  2232.      * Fish out the user's name from the mailbox portion of
  2233.      * the address and put it in folder.
  2234.      */
  2235.     folder_name = (tmp_adr && tmp_adr->mailbox && tmp_adr->mailbox[0])
  2236.               ? tmp_adr->mailbox : NULL;
  2237.     if(!get_uname(folder_name, fbuf, nfbuf))
  2238.       strcpy(fbuf, ps_global->VAR_DEFAULT_SAVE_FOLDER);
  2239.  
  2240.     break;
  2241.     }
  2242.  
  2243.     if(tmp_adr && tmp_adr != e->from && tmp_adr != e->sender
  2244.        && tmp_adr != e->to)
  2245.       mail_free_address(&tmp_adr);
  2246. }
  2247.  
  2248.  
  2249.  
  2250. /*----------------------------------------------------------------------
  2251.         Do the work of actually saving messages to a folder
  2252.  
  2253.     Args: state -- pine state struct (for stream pointers)
  2254.       context -- context to interpret name in if not fully qualified
  2255.       folder  -- The folder to save the message in
  2256.           msgmap -- message map of currently selected messages
  2257.       delete -- whether or not to delete messages saved
  2258.  
  2259.   Result: Returns number of messages saved
  2260.  
  2261.   Note: There's a bit going on here; temporary clearing of deleted flags
  2262.     since they are *not* preserved, picking or creating the stream for
  2263.     copy or append, and dealing with errors...
  2264.  ----*/
  2265. long
  2266. save(state, context, folder, msgmap, delete)
  2267.     struct pine  *state;
  2268.     CONTEXT_S    *context;
  2269.     char         *folder;
  2270.     MSGNO_S     *msgmap;
  2271.     int          delete;
  2272. {
  2273.     int          rv, rc, j, our_stream = 0, cancelled = 0;
  2274.     char     *tmp, *save_folder, *seq, flags[64], date[64];
  2275.     long      i, nmsgs, mlen;
  2276.     STORE_S     *so = NULL;
  2277.     STRING      msg;
  2278.     MAILSTREAM     *save_stream = NULL;
  2279.     MESSAGECACHE *mc;
  2280. #if    defined(DOS) && !defined(WIN32)
  2281.     struct {                    /* hack! stolen from dawz.c */
  2282.     int fd;
  2283.     unsigned long pos;
  2284.     } d;
  2285.     extern STRINGDRIVER dawz_string;
  2286. #define    SAVE_TMP_TYPE        FileStar
  2287. #else
  2288. #define    SAVE_TMP_TYPE        CharStar
  2289. #endif
  2290.  
  2291.     save_folder = (strucmp(folder, state->inbox_name) == 0)
  2292.             ? state->VAR_INBOX_PATH : folder;
  2293.  
  2294.     /*
  2295.      * Compare the current stream (the save's source) and the stream
  2296.      * the destination folder will need...
  2297.      */
  2298.     save_stream = context_same_stream(context->context, save_folder,
  2299.                       state->mail_stream);
  2300.  
  2301.     /*
  2302.      * Here we try to use context_copy if at all possible.  This should 
  2303.      * preserve flags, and, in the remote case, let the server move
  2304.      * the bits without network traffic.  It also turns out to be 
  2305.      * necessary talking to certain, known imapd's that don't/can't
  2306.      * provide APPEND...
  2307.      *
  2308.      * So, if we don't yet have a save_stream, then the source folder
  2309.      * and the destination aren't remote *and* the same server, so we
  2310.      * want to see if they're both local *and* exist (or will exist)
  2311.      * in the same local driver...
  2312.      *
  2313.      * NOTE: If the feature to aggregate the COPY sequence is set
  2314.      *         AND the folder's not in good ol' arrival order, don't
  2315.      *         even bother with local test since we know the current
  2316.      *         c-client will unwrap our ordering...
  2317.      */
  2318.     if(!save_stream && (F_OFF(F_AGG_SEQ_COPY, ps_global)
  2319.             || (mn_get_sort(msgmap) == SortArrival
  2320.                 && !mn_get_revsort(msgmap))))
  2321.       save_stream = context_same_driver(context->context, save_folder,
  2322.                     state->mail_stream);
  2323.  
  2324.     /* if needed, this'll get set in mm_notify */
  2325.     ps_global->try_to_create = 0;
  2326.     rv = rc = 0;
  2327.     nmsgs = 0L;
  2328.  
  2329.     /*
  2330.      * At this point, if we found a save_stream, then the current stream
  2331.      * is either remote, or local with both current folder and destination
  2332.      * in the same driver...
  2333.      */
  2334.     if(save_stream){
  2335.     char *dseq, *oseq;
  2336.  
  2337.     if(dseq = currentf_sequence(state->mail_stream, msgmap, F_DEL, NULL,0))
  2338.       mail_clearflag(state->mail_stream, dseq, "\\DELETED");
  2339.  
  2340.     seq = currentf_sequence(state->mail_stream, msgmap, 0L, &nmsgs, 0);
  2341.     if(F_ON(F_AGG_SEQ_COPY, ps_global)){
  2342.         /*
  2343.          * currentf_sequence() above lit all the elt "sequence"
  2344.          * bits of the interesting messages.  Now, build a sequence
  2345.          * that preserves sort order...
  2346.          */
  2347.         oseq = build_sequence(state->mail_stream, msgmap, &nmsgs);
  2348.     }
  2349.     else{
  2350.         oseq  = NULL;            /* no single sequence! */
  2351.         nmsgs = 0L;
  2352.         i = mn_first_cur(msgmap);        /* set first to copy */
  2353.     }
  2354.  
  2355.     do{
  2356.         while(!(rv = (int) context_copy(context->context, save_stream,
  2357.                 oseq ? oseq : long2string(mn_m2raw(msgmap, i)),
  2358.                 save_folder))){
  2359.         if(rc++ || !ps_global->try_to_create)   /* abysmal failure! */
  2360.           break;            /* c-client returned error? */
  2361.  
  2362.         if((context->use & CNTXT_INCMNG)
  2363.            && context_isambig(save_folder)){
  2364.             q_status_message(SM_ORDER, 3, 5,
  2365.            "Can only save to existing folders in Incoming Collection");
  2366.             break;
  2367.         }
  2368.  
  2369.         ps_global->try_to_create = 0;    /* reset for next time */
  2370.         if((j = create_for_save(save_stream,context,save_folder)) < 1){
  2371.             if(j < 0)
  2372.               cancelled = 1;        /* user cancels */
  2373.  
  2374.             break;
  2375.         }
  2376.         }
  2377.  
  2378.         if(rv){                /* failure or finished? */
  2379.         if(oseq)            /* all done? */
  2380.           break;
  2381.         else
  2382.           nmsgs++;
  2383.         }
  2384.         else{                /* failure! */
  2385.         if(oseq)
  2386.           nmsgs = 0L;            /* nothing copy'd */
  2387.  
  2388.         break;
  2389.         }
  2390.     }
  2391.     while((i = mn_next_cur(msgmap)) > 0L);
  2392.  
  2393.     if(rv && delete){            /* delete those saved */
  2394.         mail_setflag(state->mail_stream, seq, "\\DELETED");
  2395.         for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap))
  2396.           clear_index_cache_ent(i);
  2397.     }
  2398.     else if(dseq)                /* or restore previous state */
  2399.       mail_setflag(state->mail_stream, dseq, "\\DELETED");
  2400.  
  2401.     if(dseq)                /* clean up */
  2402.       fs_give((void **)&dseq);
  2403.  
  2404.     if(oseq)
  2405.       fs_give((void **)&oseq);
  2406.  
  2407.     fs_give((void **)&seq);
  2408.     }
  2409.     else{
  2410.     /*
  2411.      * Looks like the current mail stream and the stream needed by
  2412.      * the destination folder don't match, so we might as well see
  2413.      * if there's another pine stream to piggy back the APPEND of
  2414.      * the destination on.  Create our own if we need to...
  2415.      */
  2416.     save_stream = context_same_stream(context->context, save_folder,
  2417.                       state->inbox_stream);
  2418.     if(!save_stream
  2419.        && ((context_isambig(save_folder) && IS_REMOTE(context->context))
  2420.            || IS_REMOTE(save_folder))
  2421.        && (save_stream=context_open(context->context,NULL,save_folder,
  2422.                     OP_HALFOPEN)))
  2423.       our_stream = 1;
  2424.  
  2425.     /*
  2426.      * Allocate a storage object to temporarily store the message
  2427.      * object in.  Below it'll get mapped into a c-client STRING struct
  2428.      * in preparation for handing off to context_append...
  2429.      */
  2430.     if(!(so = so_get(SAVE_TMP_TYPE, NULL, WRITE_ACCESS))){
  2431.         dprint(1, (debugfile, "Can't allocate store for save: %s\n",
  2432.                error_description(errno)));
  2433.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  2434.                  "Problem creating space for message text.");
  2435.     }
  2436.  
  2437.     /* make sure flags for any message we might touch are valid */
  2438.     FETCH_ALL_FLAGS(state->mail_stream);
  2439.  
  2440.     /*
  2441.      * If we're supposed set the deleted flag, clear the elt bit
  2442.      * we'll use to build the sequence later...
  2443.      */
  2444.     if(delete)
  2445.       for(i = 1L; i <= state->mail_stream->nmsgs; i++)
  2446.         mail_elt(state->mail_stream, i)->sequence = 0;
  2447.  
  2448.     nmsgs = 0L;
  2449.     for(i = mn_first_cur(msgmap); so && i > 0L; i = mn_next_cur(msgmap)){
  2450.         so_truncate(so, 0L);
  2451.  
  2452.         /* store flags before the fetch so UNSEEN bit isn't flipped */
  2453.         mc = mail_elt(state->mail_stream, mn_m2raw(msgmap, i));
  2454.         flag_string(mc, F_ANS|F_FLAG|F_SEEN, flags);
  2455.  
  2456.         if(tmp = mail_fetchheader(state->mail_stream, mn_m2raw(msgmap,i))){
  2457.         /*
  2458.          * If the MESSAGECACHE element doesn't already have it, 
  2459.          * parse the "internal date" by hand since fetchstructure
  2460.          * hasn't been called yet for this particular message, and
  2461.          * we don't want to call it now just to get the date since
  2462.          * the full header has what we want.  Likewise, don't even
  2463.          * think about calling mail_fetchfast either since it also
  2464.          * wants to load mc->rfc822_size (which we could actually
  2465.          * use but...), which under some drivers is *very*
  2466.          * expensive to acquire (can you say NNTP?)...
  2467.          */
  2468.         mc = mail_elt(state->mail_stream, mn_m2raw(msgmap, i));
  2469.         if(mc->day)
  2470.           mail_date(date, mc);
  2471.         else
  2472.           saved_date(date, tmp);
  2473.  
  2474.         if(!so_puts(so, tmp))
  2475.           break;
  2476.         }
  2477.         else
  2478.           break;                /* fetchtext writes error */
  2479.  
  2480. #if    defined(DOS) && !defined(WIN32)
  2481.         /*
  2482.          * Set append file and install dos_gets so message text
  2483.          * is fetched directly to disk.
  2484.          */
  2485.         mail_parameters(state->mail_stream, SET_GETS, (void *)dos_gets);
  2486.         append_file = (FILE *) so_text(so);
  2487.         mail_gc(state->mail_stream, GC_TEXTS);
  2488.  
  2489.         if(!(tmp = mail_fetchtext(state->mail_stream, mn_m2raw(msgmap,i))))
  2490.           break;
  2491.  
  2492.         /*
  2493.          * Clean up after our DOS hacks...
  2494.          */
  2495.         append_file = NULL;
  2496.         mail_parameters(state->mail_stream, SET_GETS, (void *)NULL);
  2497.         mail_gc(state->mail_stream, GC_TEXTS);
  2498. #else
  2499.         if(!((tmp = mail_fetchtext(state->mail_stream, mn_m2raw(msgmap,i)))
  2500.          && so_puts(so, tmp)))
  2501.           break;
  2502. #endif
  2503.  
  2504.         so_seek(so, 0L, 0);            /* just in case */
  2505.  
  2506.         /*
  2507.          * Set up a c-client string driver so we can hand the
  2508.          * collected text down to mail_append.
  2509.          *
  2510.          * NOTE: We only test the size if and only if we already
  2511.          *         have it.  See, in some drivers, especially under
  2512.          *         dos, its too expensive to get the size (full
  2513.          *         header and body text fetch plus MIME parse), so
  2514.          *         we only verify the size if we already know it.
  2515.          */
  2516.         mc = mail_elt(state->mail_stream, mn_m2raw(msgmap, i));
  2517. #if    defined(DOS) && !defined(WIN32)
  2518.         d.fd  = fileno((FILE *)so_text(so));
  2519.         d.pos = 0L;
  2520.         mlen = filelength(d.fd);
  2521.         if(mc->rfc822_size && mlen < mc->rfc822_size){
  2522.         char buf[128];
  2523.  
  2524.         sprintf(buf, "Message to save shrank!  (#%ld: %ld --> %ld)",
  2525.             mc->msgno, mc->rfc822_size, mlen);
  2526.         q_status_message(SM_ORDER | SM_DING, 3, 4, buf);
  2527.         dprint(1, (debugfile, "BOTCH: %s\n", buf));
  2528.         break;
  2529.         }
  2530.  
  2531.         INIT(&msg, dawz_string, (void *)&d, mlen);
  2532. #else
  2533.         mlen = strlen((char *)so_text(so));
  2534.         if(mc->rfc822_size && mlen < mc->rfc822_size){
  2535.         char buf[128];
  2536.  
  2537.         sprintf(buf, "Message to save shrank!  (#%ld: %ld --> %ld)",
  2538.             mc->msgno, mc->rfc822_size, mlen);
  2539.         q_status_message(SM_ORDER | SM_DING, 3, 4, buf);
  2540.         dprint(1, (debugfile, "BOTCH: %s\n", buf));
  2541.         break;
  2542.         }
  2543.  
  2544.         INIT(&msg, mail_string, (void *)so_text(so), mlen);
  2545. #endif
  2546.         while(!(rv = (int) context_append_full(context->context,
  2547.                            save_stream, save_folder,
  2548.                            flags, *date ? date : NULL,
  2549.                            &msg))){
  2550.         if(rc++ || !ps_global->try_to_create) /* abysmal failure! */
  2551.           break;            /* c-client returned error? */
  2552.  
  2553.         if((context->use & CNTXT_INCMNG)
  2554.            && context_isambig(save_folder)){
  2555.             q_status_message(SM_ORDER, 3, 5,
  2556.            "Can only save to existing folders in Incoming Collection");
  2557.             break;
  2558.         }
  2559.  
  2560.         ps_global->try_to_create = 0;    /* reset for next time */
  2561.         if((j = create_for_save(save_stream,context,save_folder)) < 1){
  2562.             if(j < 0)
  2563.               cancelled = 1;        /* user cancelled */
  2564.  
  2565.             break;
  2566.         }
  2567.  
  2568.         SETPOS((&msg), 0L);        /* reset string driver */
  2569.         }
  2570.  
  2571.         if(!rv)
  2572.           break;                /* horrendous failure */
  2573.  
  2574.         /*
  2575.          * Success!  Count it, and if it's not already deleted and 
  2576.          * it's supposed to be, mark it to get deleted later...
  2577.          */
  2578.         nmsgs++;
  2579.         if(delete){
  2580.         mc = mail_elt(state->mail_stream, mn_m2raw(msgmap, i));
  2581.         if(!mc->deleted){
  2582.             mc->sequence = 1;        /* mark for later deletion */
  2583.             clear_index_cache_ent(i);
  2584.             check_point_change();
  2585.         }
  2586.         }
  2587.     }
  2588.  
  2589.     if(our_stream)
  2590.       mail_close(save_stream);
  2591.  
  2592.     if(so)
  2593.       so_give(&so);
  2594.  
  2595.     if(delete && (seq = build_sequence(state->mail_stream, NULL, NULL))){
  2596.         mail_setflag(state->mail_stream, seq, "\\DELETED");
  2597.         fs_give((void **)&seq);
  2598.     }
  2599.     }
  2600.  
  2601.     ps_global->try_to_create = 0;        /* reset for next time */
  2602.     if(!cancelled && nmsgs < mn_total_cur(msgmap)){
  2603.     dprint(1, (debugfile, "FAILED save of msg %s (c-client sequence #)\n",
  2604.            long2string(mn_m2raw(msgmap, mn_get_cur(msgmap)))));
  2605.     if(mn_total_cur(msgmap) > 1L){
  2606.         sprintf(tmp_20k_buf,
  2607.             "%ld of %ld messages saved before error occurred",
  2608.             nmsgs, mn_total_cur(msgmap));
  2609.         dprint(1, (debugfile, "\t%s\n", tmp_20k_buf));
  2610.         q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
  2611.     }
  2612.     }
  2613.  
  2614.     return(nmsgs);
  2615. }
  2616.  
  2617.  
  2618.  
  2619. /*----------------------------------------------------------------------
  2620.     Offer to create a non-existant folder to copy message[s] into
  2621.  
  2622.    Args: stream -- stream to use for creation
  2623.      context -- context to create folder in
  2624.      name -- name of folder to create
  2625.  
  2626.  Result: 0 if create failed (c-client writes error)
  2627.      1 if create successful
  2628.     -1 if user declines to create folder
  2629.  ----*/
  2630. int
  2631. create_for_save(stream, context, folder)
  2632.     MAILSTREAM *stream;
  2633.     CONTEXT_S  *context;
  2634.     char       *folder;
  2635. {
  2636.     if(ps_global->context_list->next && context_isambig(folder)){
  2637.     sprintf(tmp_20k_buf,
  2638.         "Folder \"%.15s%s\" in <%.15s%s> doesn't exist. Create",
  2639.         folder, (strlen(folder) > 15) ? "..." : "",
  2640.         context->label[0],
  2641.         (strlen(context->label[0]) > 15) ? "..." : "");
  2642.     }
  2643.     else
  2644.       sprintf(tmp_20k_buf,"Folder \"%.40s\" doesn't exist.  Create", folder);
  2645.  
  2646.     if(want_to(tmp_20k_buf, 'y', 'n', NO_HELP, 0, 0) != 'y'){
  2647.     cmd_cancelled("Save message");
  2648.     return(-1);
  2649.     }
  2650.  
  2651.     return(context_create(context->context, NULL, folder));
  2652. }
  2653.  
  2654.  
  2655.  
  2656. /*----------------------------------------------------------------------
  2657.   Set 
  2658.  
  2659.    Args: mc -- message cache element to dig the flags out of
  2660.  
  2661.  Result: Malloc'd string representing flags set in mc
  2662.  ----*/
  2663. void
  2664. flag_string(mc, flags, flagbuf)
  2665.     MESSAGECACHE *mc;
  2666.     long      flags;
  2667.     char     *flagbuf;
  2668. {
  2669.     char *p;
  2670.  
  2671.     *(p = flagbuf) = '\0';
  2672.  
  2673.     if((flags & F_DEL) && mc->deleted)
  2674.       sstrcpy(&p, "\\DELETED ");
  2675.  
  2676.     if((flags & F_ANS) && mc->answered)
  2677.       sstrcpy(&p, "\\ANSWERED ");
  2678.  
  2679.     if((flags & F_FLAG) && mc->flagged)
  2680.       sstrcpy(&p, "\\FLAGGED ");
  2681.  
  2682.     if((flags & F_SEEN) && mc->seen)
  2683.       sstrcpy(&p, "\\SEEN ");
  2684.  
  2685.     if(p != flagbuf)
  2686.       *--p = '\0';            /* tie off tmp_20k_buf   */
  2687. }
  2688.  
  2689.  
  2690.  
  2691. /*----------------------------------------------------------------------
  2692.    Save() helper function to create canonical date string from given header
  2693.  
  2694.    Args: date -- buf to recieve canonical date string
  2695.      header -- rfc822 header to fish date string from
  2696.  
  2697.  Result: date filled with canonicalized date in header, or null string
  2698.  ----*/
  2699. void
  2700. saved_date(date, header)
  2701.     char *date, *header;
  2702. {
  2703.     char     *d, *p, c;
  2704.     MESSAGECACHE  elt;
  2705.  
  2706.     *date = '\0';
  2707.     if((toupper((unsigned char)(*(d = header)))
  2708.     == 'D' && !strncmp(d, "date:", 5))
  2709.        || (d = srchstr(header, "\015\012date:"))){
  2710.     for(d += 7; *d == ' '; d++)
  2711.       ;                    /* skip white space */
  2712.  
  2713.     if(p = strstr(d, "\015\012")){
  2714.         for(; p > d && *p == ' '; p--)
  2715.           ;                    /* skip white space */
  2716.  
  2717.         c  = *p;
  2718.         *p = '\0';                /* tie off internal date */
  2719.     }
  2720.  
  2721.     if(mail_parse_date(&elt, d))        /* let c-client normalize it */
  2722.       mail_date(date, &elt);
  2723.  
  2724.     if(p)                    /* restore header */
  2725.       *p = c;
  2726.     }
  2727. }
  2728.  
  2729.  
  2730.  
  2731. /*----------------------------------------------------------------------
  2732.     Export a message to a plain file in users home directory
  2733.  
  2734.    Args:  q_line -- screen line to prompt on
  2735.          message -- MESSAGECACHE enrty of message to export
  2736.  
  2737.  Result: 
  2738.  ----*/
  2739. void
  2740. cmd_export(state, msgmap, q_line, agg)
  2741.     struct pine *state;
  2742.     MSGNO_S     *msgmap;
  2743.     int          q_line;
  2744.     int         agg;
  2745. {
  2746.     HelpType  help;
  2747.     char      filename[MAXPATH+1], full_filename[MAXPATH+1],*ill;
  2748.     int       rc, new_file, failure = 0, orig_errno, over = 0;
  2749.     ENVELOPE *env;
  2750.     BODY     *b;
  2751.     long      i, count = 0L, start_of_append;
  2752.     gf_io_t   pc;
  2753.     STORE_S  *store;
  2754.     struct variable *vars = ps_global->vars;
  2755.     static ESCKEY_S export_opts[] = {
  2756.     {ctrl('T'), 10, "^T", "To Files"},
  2757.     {-1, 0, NULL, NULL},
  2758.     {-1, 0, NULL, NULL},
  2759.     {-1, 0, NULL, NULL}};
  2760.  
  2761.     if(ps_global->restricted){
  2762.     q_status_message(SM_ORDER, 0, 3,
  2763.         "Pine demo can't export messages to files");
  2764.     return;
  2765.     }
  2766.  
  2767.     if(agg && !pseudo_selected(msgmap))
  2768.       return;
  2769.  
  2770.     i = 0;
  2771.  
  2772. #if    !defined(DOS) && !defined(MAC) && !defined(OS2)
  2773.     if(ps_global->VAR_DOWNLOAD_CMD && ps_global->VAR_DOWNLOAD_CMD[0]){
  2774.     export_opts[++i].ch  = ctrl('V');
  2775.     export_opts[i].rval  = 12;
  2776.     export_opts[i].name  = "^V";
  2777.     export_opts[i].label = "Downld Msg";
  2778.     }
  2779. #endif    /* !(DOS || MAC) */
  2780.  
  2781.     if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
  2782.     export_opts[++i].ch  =  ctrl('I');
  2783.     export_opts[i].rval  = 11;
  2784.     export_opts[i].name  = "TAB";
  2785.     export_opts[i].label = "Complete";
  2786.     }
  2787.  
  2788.     export_opts[++i].ch = -1;
  2789.  
  2790.     help = NO_HELP;
  2791.     filename[0] = '\0';
  2792.     while(1) {
  2793.         char prompt[100];
  2794. #ifdef    DOS
  2795.         (void)strcpy(prompt, "File to save message text in: ");
  2796. #else
  2797.     sprintf(prompt, "EXPORT: (copy message) to file in %s directory: ",
  2798.             F_ON(F_USE_CURRENT_DIR, ps_global) ? "current"
  2799.         : VAR_OPER_DIR ? VAR_OPER_DIR : "home");
  2800. #endif
  2801.         rc = optionally_enter(filename, q_line, 0, MAXPATH, 1, 0,
  2802.                  prompt, export_opts, help, 0);
  2803.  
  2804.         /*--- Help ----*/
  2805.     if(rc == 10){            /* ^T to files */
  2806.         if(filename[0])
  2807.           strcpy(full_filename, filename);
  2808.         else if(F_ON(F_USE_CURRENT_DIR, ps_global))
  2809.           (void) getcwd(full_filename, MAXPATH);
  2810.         else if(VAR_OPER_DIR)
  2811.           build_path(full_filename, VAR_OPER_DIR, filename);
  2812.         else
  2813.               build_path(full_filename, ps_global->home_dir, filename);
  2814.  
  2815.         rc = file_lister("EXPORT", full_filename, filename, TRUE, FB_SAVE);
  2816.  
  2817.         if(rc == 1){
  2818.         strcat(full_filename, "/");
  2819.         strcat(full_filename, filename);
  2820.         break;
  2821.         }
  2822.         else
  2823.           continue;
  2824.     }
  2825.     else if(rc == 11){        /* tab completion */
  2826.         char dir[MAXPATH], *fn;
  2827.         int  l = MAXPATH;
  2828.  
  2829.         dir[0] = '\0';
  2830.         if(*filename && (fn = last_cmpnt(filename))){
  2831.         l -= fn - filename;
  2832.         if(is_absolute_path(filename)){
  2833.             strncpy(dir, filename, fn - filename);
  2834.             dir[fn - filename] = '\0';
  2835.         }
  2836.         else{
  2837.             char *p = NULL;
  2838.             sprintf(full_filename, "%.*s", fn - filename, filename);
  2839.             build_path(dir, F_ON(F_USE_CURRENT_DIR, ps_global)
  2840.                        ? p = (char *) getcwd(NULL, MAXPATH)
  2841.                        : VAR_OPER_DIR ? VAR_OPER_DIR
  2842.                               : ps_global->home_dir,
  2843.                    full_filename);
  2844.             if(p)
  2845.               free(p);
  2846.         }
  2847.         }
  2848.         else{
  2849.         fn = filename;
  2850.         if(F_ON(F_USE_CURRENT_DIR, ps_global))
  2851.           (void) getcwd(dir, MAXPATH);
  2852.         else if(VAR_OPER_DIR)
  2853.           strcpy(dir, VAR_OPER_DIR);
  2854.         else
  2855.           strcpy(dir, ps_global->home_dir);
  2856.         }
  2857.  
  2858.         if(!pico_fncomplete(dir, fn, l - 1))
  2859.           Writechar(BELL, 0);
  2860.  
  2861.         continue;
  2862.     }
  2863. #if    !defined(DOS) && !defined(MAC) && !defined(OS2)
  2864.     else if(rc == 12){            /* download current messge */
  2865.         char     cmd[MAXPATH], *tfp, *errstr = NULL;
  2866.         int         next = 0;
  2867.         PIPE_S  *syspipe;
  2868.         STORE_S *so;
  2869.         gf_io_t  pc;
  2870.  
  2871.         if(ps_global->restricted){
  2872.         q_status_message(SM_ORDER | SM_DING, 3, 3,
  2873.                  "Download disallowed in restricted mode");
  2874.         goto fini;
  2875.         }
  2876.  
  2877.         tfp = temp_nam(NULL, "pd");
  2878.         build_updown_cmd(cmd, ps_global->VAR_DOWNLOAD_CMD_PREFIX,
  2879.                  ps_global->VAR_DOWNLOAD_CMD, tfp);
  2880.         dprint(1, (debugfile, "Download cmd called: \"%s\"\n", cmd));
  2881.         if(so = so_get(FileStar, tfp, WRITE_ACCESS)){
  2882.         gf_set_so_writec(&pc, so);
  2883.  
  2884.         for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap))
  2885.           if(!(env = mail_fetchstructure(state->mail_stream,
  2886.                          mn_m2raw(msgmap, i), &b))
  2887.              || !bezerk_delimiter(env, pc, next++)
  2888.              || !format_message(mn_m2raw(msgmap, mn_get_cur(msgmap)),
  2889.                     env, b, FM_NEW_MESS|FM_DO_PRINT, pc)){
  2890.               q_status_message(SM_ORDER | SM_DING, 3, 3,
  2891.                    errstr = "Error writing tempfile for download");
  2892.               break;
  2893.           }
  2894.  
  2895.         so_give(&so);            /* close file */
  2896.  
  2897.         if(!errstr){
  2898.             if(syspipe = open_system_pipe(cmd, NULL, NULL,
  2899.                           PIPE_USER | PIPE_RESET))
  2900.               (void) close_system_pipe(&syspipe);
  2901.             else
  2902.               q_status_message(SM_ORDER | SM_DING, 3, 3,
  2903.                     errstr = "Error running download command");
  2904.         }
  2905.  
  2906.         unlink(tfp);
  2907.         }
  2908.         else
  2909.           q_status_message(SM_ORDER | SM_DING, 3, 3,
  2910.                  errstr = "Error building temp file for download");
  2911.  
  2912.         fs_give((void **)&tfp);
  2913.         if(!errstr)
  2914.           q_status_message(SM_ORDER, 0, 3, "Download Command Completed");
  2915.  
  2916.         goto fini;
  2917.     }
  2918. #endif    /* !(DOS || MAC) */
  2919.     else if(rc == 3) {
  2920.             help = (help == NO_HELP) ? h_oe_export : NO_HELP;
  2921.             continue;
  2922.         }
  2923.  
  2924.         removing_trailing_white_space(filename);
  2925.         removing_leading_white_space(filename);
  2926.         if(rc == 1 || filename[0] == '\0') {
  2927.         cmd_cancelled("Export message");
  2928.         goto fini;
  2929.         }
  2930.  
  2931.         if(rc == 4)
  2932.           continue;
  2933.  
  2934.  
  2935.         /*-- check out and expand file name. give possible error messages --*/
  2936.         strcpy(full_filename, filename);
  2937.         if((ill = filter_filename(filename)) != NULL) {
  2938.             q_status_message1(SM_ORDER | SM_DING, 3, 3, "%s", ill);
  2939.             continue;
  2940.         }
  2941.  
  2942. #if defined(DOS) || defined(OS2)
  2943.     if(is_absolute_path(full_filename)){
  2944.         fixpath(full_filename, MAXPATH);
  2945.     }
  2946. #else
  2947.         if(full_filename[0] == '~') {
  2948.             if(fnexpand(full_filename, sizeof(full_filename)) == NULL) {
  2949.         char *p = last_cmpnt(full_filename);
  2950.         if(p != NULL)
  2951.           *p = '\0';
  2952.  
  2953.         q_status_message1(SM_ORDER | SM_DING, 3, 3,
  2954.                   "Error expanding file name: \"%s\" unknown user",
  2955.                   full_filename);
  2956.         continue;
  2957.         }
  2958.     }
  2959. #endif
  2960.  
  2961.     if(!is_absolute_path(full_filename)){
  2962.         if(F_ON(F_USE_CURRENT_DIR, ps_global))
  2963.               (void)strcpy(full_filename, filename);
  2964.         else if(VAR_OPER_DIR)
  2965.           build_path(full_filename, VAR_OPER_DIR, filename);
  2966.         else
  2967.               build_path(full_filename, ps_global->home_dir, filename);
  2968.         }
  2969.  
  2970.         break; /* Must have got an OK file name */
  2971.  
  2972.     }
  2973.  
  2974.  
  2975.     if(VAR_OPER_DIR && !in_dir(VAR_OPER_DIR, full_filename)){
  2976.     q_status_message1(SM_ORDER, 0, 2, "Can't export to file outside of %s",
  2977.               VAR_OPER_DIR);
  2978.     goto fini;
  2979.     }
  2980.  
  2981.  
  2982.     /* ---- full_filename already contains the absolute path ---*/
  2983.     if(!can_access(full_filename, ACCESS_EXISTS)) {
  2984.         char prompt[100];
  2985.     static ESCKEY_S access_opts[] = {
  2986.         {'o', 'o', "O", "Overwrite"},
  2987.         {'a', 'a', "A", "Append"},
  2988.         {-1, 0, NULL, NULL}};
  2989.  
  2990.     rc = strlen(filename);
  2991.         sprintf(prompt,
  2992.         "File \"%s%s\" already exists.  Overwrite or append it ? ",
  2993.         (rc > 20) ? "..." : "",
  2994.                 filename + ((rc > 20) ? rc - 20 : 0));
  2995.     switch(radio_buttons(prompt, -FOOTER_ROWS(state), access_opts,
  2996.                  'a', 'x', NO_HELP, RB_NORM)){
  2997.       case 'o' :
  2998.         new_file = 1;
  2999.         over = 1;
  3000.         if(unlink(full_filename) < 0){    /* BUG: breaks links */
  3001.         q_status_message2(SM_ORDER | SM_DING, 3, 5,
  3002.                   "Error deleting old %s: %s",
  3003.                   full_filename, error_description(errno));
  3004.         goto fini;
  3005.         }
  3006.  
  3007.         break;
  3008.  
  3009.       case 'a' :
  3010.         new_file = 0;
  3011.          over = -1;
  3012.         break;
  3013.  
  3014.       case 'x' :
  3015.       default :
  3016.         cmd_cancelled("Export message");
  3017.         goto fini;
  3018.     }
  3019.     }
  3020.     else
  3021.       new_file = 1;
  3022.  
  3023.     dprint(5, (debugfile, "Opening file \"%s\" for export\n", full_filename));
  3024.  
  3025.     if(!(store = so_get(FileStar, full_filename, WRITE_ACCESS))){
  3026.         q_status_message2(SM_ORDER | SM_DING, 3, 4,
  3027.               "Error opening file \"%s\" to export message: %s",
  3028.                           full_filename, error_description(errno));
  3029.     goto fini;
  3030.     }
  3031.     else
  3032.       gf_set_so_writec(&pc, store);
  3033.  
  3034.     for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap), count++){
  3035.     env = mail_fetchstructure(state->mail_stream, mn_m2raw(msgmap, i), &b);
  3036.     if(!env) {
  3037.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  3038.               "Can't export message. Error accessing mail folder");
  3039.         goto fini;
  3040.     }
  3041.  
  3042.     start_of_append = ftell((FILE *)so_text(store));
  3043.     if(!bezerk_delimiter(env, pc, new_file)
  3044.        || !format_message(mn_m2raw(msgmap, i), env, b,
  3045.                   (FM_NEW_MESS|FM_DO_PRINT), pc)){
  3046.         orig_errno = errno;        /* save incase things are really bad */
  3047.         failure    = 1;        /* pop out of here */
  3048.         break;
  3049.     }
  3050.     }
  3051.  
  3052.     so_give(&store);                /* release storage */
  3053.     if(failure){
  3054. #ifdef    DOS
  3055.     chsize(fileno((FILE *)so_text(store)), start_of_append);
  3056. #else
  3057.     truncate(full_filename, start_of_append);
  3058. #endif
  3059.     dprint(1, (debugfile, "FAILED Export: file \"%s\" : %s\n",
  3060.            full_filename,  error_description(orig_errno)));
  3061.     q_status_message2(SM_ORDER | SM_DING, 3, 4,
  3062.               "Error exporting to \"%s\" : %s",
  3063.               filename, error_description(orig_errno));
  3064.     }
  3065.     else{
  3066.     if(mn_total_cur(msgmap) > 1L)
  3067.       q_status_message4(SM_ORDER,0,3,"%s message%s %s to file \"%s\"",
  3068.                 long2string(count), plural(count),
  3069.                 over==0 ? "exported"
  3070.                     : over==1 ? "overwritten" : "appended",
  3071.                 filename);
  3072.     else
  3073.       q_status_message3(SM_ORDER,0,3,"Message %s %s to file \"%s\"",
  3074.                 long2string(mn_get_cur(msgmap)),
  3075.                 over==0 ? "exported"
  3076.                     : over==1 ? "overwritten" : "appended",
  3077.                 filename);
  3078.     }
  3079.  
  3080.   fini:
  3081.     if(agg)
  3082.       restore_selected(msgmap);
  3083. }
  3084.  
  3085.  
  3086.  
  3087. /*----------------------------------------------------------------------
  3088.   parse the config'd upload/download command
  3089.  
  3090.   Args: cmd -- buffer to return command fit for shellin'
  3091.     prefix --
  3092.     cfg_str --
  3093.     fname -- file name to build into the command
  3094.  
  3095.   Returns: pointer to cmd_str buffer or NULL on real bad error
  3096.  
  3097.   NOTE: One SIDE EFFECT is that any defined "prefix" string in the
  3098.     cfg_str is written to standard out right before a successful
  3099.     return of this function.  The call immediately following this
  3100.     function darn well better be the shell exec...
  3101.  ----*/
  3102. char *
  3103. build_updown_cmd(cmd, prefix, cfg_str, fname)
  3104.     char *cmd;
  3105.     char *prefix;
  3106.     char *cfg_str;
  3107.     char *fname;
  3108. {
  3109.     char *p;
  3110.     int   fname_found = 0;
  3111.  
  3112.     if(prefix && *prefix){
  3113.     /* loop thru replacing all occurances of _FILE_ */
  3114.     for(p = strcpy(cmd, prefix); (p = strstr(p, "_FILE_")); )
  3115.       rplstr(p, 6, fname);
  3116.  
  3117.     fputs(cmd, stdout);
  3118.     }
  3119.  
  3120.     /* loop thru replacing all occurances of _FILE_ */
  3121.     for(p = strcpy(cmd, cfg_str); (p = strstr(p, "_FILE_")); ){
  3122.     rplstr(p, 6, fname);
  3123.     fname_found = 1;
  3124.     }
  3125.  
  3126.     if(!fname_found)
  3127.       sprintf(cmd + strlen(cmd), " %s", fname);
  3128.  
  3129.     dprint(4, (debugfile, "\n - build_updown_cmd = \"%s\" -\n", cmd));
  3130.     return(cmd);
  3131. }
  3132.  
  3133.  
  3134.  
  3135.  
  3136.  
  3137.  
  3138. /*----------------------------------------------------------------------
  3139.   Write a berzerk format message delimiter using the given putc function
  3140.  
  3141.     Args: e -- envelope of message to write
  3142.       pc -- function to use 
  3143.  
  3144.     Returns: TRUE if we could write it, FALSE if there was a problem
  3145.  
  3146.     NOTE: follows delimiter with OS-dependent newline
  3147.  ----*/
  3148. int
  3149. bezerk_delimiter(env, pc, leading_newline)
  3150.     ENVELOPE *env;
  3151.     gf_io_t   pc;
  3152.     int          leading_newline;
  3153. {
  3154.     time_t  now = time(0);
  3155.     char   *p = ctime(&now);
  3156.     
  3157.     /* write "[\n]From mailbox[@host] " */
  3158.     if(!((leading_newline ? gf_puts(NEWLINE, pc) : 1)
  3159.      && gf_puts("From ", pc)
  3160.      && gf_puts((env && env->from) ? env->from->mailbox
  3161.                        : "the-concourse-on-high", pc)
  3162.      && gf_puts((env && env->from && env->from->host) ? "@" : "", pc)
  3163.      && gf_puts((env && env->from && env->from->host) ? env->from->host
  3164.                               : "", pc)
  3165.      && (*pc)(' ')))
  3166.       return(0);
  3167.  
  3168.     while(p && *p && *p != '\n')    /* write date */
  3169.       if(!(*pc)(*p++))
  3170.     return(0);
  3171.  
  3172.     if(!gf_puts(NEWLINE, pc))        /* write terminating newline */
  3173.       return(0);
  3174.  
  3175.     return(1);
  3176. }
  3177.  
  3178.  
  3179.  
  3180. /*----------------------------------------------------------------------
  3181.       Execute command to jump to a given message number
  3182.  
  3183.     Args: qline -- Line to ask question on
  3184.  
  3185.   Result: returns -1 or the message number to jump to
  3186.           the mangled_footer flag is set
  3187.  ----*/
  3188. void
  3189. jump_to(msgmap, qline, first_num)
  3190.      MSGNO_S *msgmap;
  3191.      int      qline, first_num;
  3192. {
  3193.     char     jump_num_string[80], *j, prompt[70];
  3194.     HelpType help;
  3195.     int      rc;
  3196.     long     jump_num;
  3197.  
  3198.     dprint(4, (debugfile, "\n - jump_to -\n"));
  3199.  
  3200.     if(!any_messages(msgmap, NULL, "to Jump to"))
  3201.       return;
  3202.  
  3203.     if(first_num){
  3204.     jump_num_string[0] = first_num;
  3205.     jump_num_string[1] = '\0';
  3206.     }
  3207.     else
  3208.       jump_num_string[0] = '\0';
  3209.  
  3210.     if(mn_total_cur(msgmap) > 1L){
  3211.     sprintf(prompt, "Unselect %s msgs in favor of number to be entered", 
  3212.         comatose(mn_total_cur(msgmap)));
  3213.     if((rc = want_to(prompt, 'n', 0, NO_HELP, 0, 0)) == 'n')
  3214.         return;
  3215.     }
  3216.  
  3217.     strcpy(prompt, "Message number to jump to : ");
  3218.  
  3219.     help = NO_HELP;
  3220.     while (1) {
  3221.         rc = optionally_enter(jump_num_string, qline, 0,
  3222.                               sizeof(jump_num_string) - 1, 1, 0, prompt,
  3223.                               NULL, help, 0);
  3224.         if(rc == 3) {
  3225.             help = help == NO_HELP ? h_oe_jump : NO_HELP;
  3226.             continue;
  3227.         }
  3228.  
  3229.         if(rc == 0 && *jump_num_string != '\0') {
  3230.         removing_trailing_white_space(jump_num_string);
  3231.         removing_leading_white_space(jump_num_string);
  3232.             for(j=jump_num_string; isdigit((unsigned char)*j) || *j=='-'; j++);
  3233.         if(*j != '\0') {
  3234.             q_status_message(SM_ORDER | SM_DING, 2, 2,
  3235.                            "Invalid number entered. Use only digits 0-9");
  3236.             } else {
  3237.                 jump_num = atol(jump_num_string);
  3238.                 if(jump_num < 1L) {
  3239.                 q_status_message1(SM_ORDER | SM_DING, 2, 2,
  3240.                   "Message number (%s) must be greater than 0",
  3241.                       long2string(jump_num));
  3242.                 } else if(jump_num > mn_get_total(msgmap)) {
  3243.                     q_status_message1(SM_ORDER | SM_DING, 2, 2,
  3244.           "Message number must be no more than %s, the number of messages",
  3245.                       long2string(mn_get_total(msgmap)));
  3246.                 } else if(get_lflag(ps_global->mail_stream, msgmap,
  3247.                     jump_num, MN_HIDE)){
  3248.                 q_status_message1(SM_ORDER | SM_DING, 2, 2,
  3249.               "Message number (%s) is not in \"Zoomed Index\"",
  3250.                       long2string(jump_num));
  3251.         } else {
  3252.             if(mn_total_cur(msgmap) > 1L){
  3253.             mn_reset_cur(msgmap, jump_num);
  3254.             }
  3255.             else{
  3256.             mn_set_cur(msgmap, jump_num);
  3257.             }
  3258.  
  3259.             break;
  3260.                 }
  3261.             }
  3262.  
  3263.             jump_num_string[0] = '\0';
  3264.             continue;
  3265.     }
  3266.  
  3267.         if(rc != 4)
  3268.           break;
  3269.     }
  3270. }
  3271.  
  3272.  
  3273.  
  3274. /*----------------------------------------------------------------------
  3275.      Prompt for folder name to open, expand the name and return it
  3276.  
  3277.    Args: qline      -- Screen line to prompt on
  3278.          allow_list -- if 1, allow ^T to bring up collection lister
  3279.  
  3280.  Result: returns the folder name or NULL
  3281.          pine structure mangled_footer flag is set
  3282.          may call the collection lister in which case mangled screen will be set
  3283.  
  3284.  This prompts the user for the folder to open, possibly calling up
  3285. the collection lister if the user types ^T.
  3286. ----------------------------------------------------------------------*/
  3287. char *
  3288. broach_folder(qline, allow_list, context)
  3289.     int qline, allow_list;
  3290.     CONTEXT_S **context;
  3291. {
  3292.     HelpType    help;
  3293.     static char newfolder[MAXFOLDER+1];
  3294.     char        expanded[MAXPATH+1],
  3295.                 prompt[MAXFOLDER+80],
  3296.                *last_folder;
  3297.     CONTEXT_S  *tc, *tc2;
  3298.     ESCKEY_S    ekey[7];
  3299.     int         rc, inbox;
  3300.  
  3301.     /*
  3302.      * the idea is to provide a clue for the context the file name
  3303.      * will be saved in (if a non-imap names is typed), and to
  3304.      * only show the previous if it was also in the same context
  3305.      */
  3306.     help       = NO_HELP;
  3307.     *expanded       = '\0';
  3308.     *newfolder       = '\0';
  3309.     last_folder       = NULL;
  3310.     ps_global->mangled_footer = 1;
  3311.  
  3312.     /*
  3313.      * There are three possibilities for the prompt's offered default.
  3314.      *  1) always the last folder visited
  3315.      *  2) if non-inbox current, inbox else last folder visited
  3316.      *  3) if non-inbox current, inbox else last folder visited in
  3317.      *     the first collection
  3318.      */
  3319.     if(ps_global->goto_default_rule == GOTO_LAST_FLDR){
  3320.     tc = (context && *context) ? *context : ps_global->context_current;
  3321.     inbox = 1;        /* fill in last_folder below */
  3322.     }
  3323.     else{
  3324.     inbox = strucmp(ps_global->cur_folder,ps_global->inbox_name) == 0;
  3325.     if(!inbox)
  3326.       tc = ps_global->context_list;        /* inbox's context */
  3327.     else if(ps_global->goto_default_rule == GOTO_INBOX_FIRST_CLCTN)
  3328.       tc = (ps_global->context_list->use & CNTXT_INCMNG)
  3329.          ? ps_global->context_list->next : ps_global->context_list;
  3330.     else
  3331.       tc = (context && *context) ? *context : ps_global->context_current;
  3332.     }
  3333.  
  3334.     /* set up extra command option keys */
  3335.     rc = 0;
  3336.     ekey[rc].ch         = (allow_list) ? ctrl('T') : 0 ;
  3337.     ekey[rc].rval    = (allow_list) ? 2 : 0;
  3338.     ekey[rc].name    = (allow_list) ? "^T" : "";
  3339.     ekey[rc++].label = (allow_list) ? "ToFldrs" : "";
  3340.  
  3341.     if(ps_global->context_list->next){
  3342.     ekey[rc].ch      = ctrl('P');
  3343.     ekey[rc].rval    = 10;
  3344.     ekey[rc].name    = "^P";
  3345.     ekey[rc++].label = "Prev Collection";
  3346.  
  3347.     ekey[rc].ch      = ctrl('N');
  3348.     ekey[rc].rval    = 11;
  3349.     ekey[rc].name    = "^N";
  3350.     ekey[rc++].label = "Next Collection";
  3351.     }
  3352.  
  3353.     if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
  3354.     ekey[rc].ch      = TAB;
  3355.     ekey[rc].rval    = 12;
  3356.     ekey[rc].name    = "TAB";
  3357.     ekey[rc++].label = "Complete";
  3358.     }
  3359.  
  3360.     if(ps_global->context_list->next){
  3361.     ekey[rc].ch      = KEY_UP;
  3362.     ekey[rc].rval    = 10;
  3363.     ekey[rc].name    = "";
  3364.     ekey[rc++].label = "";
  3365.  
  3366.     ekey[rc].ch      = KEY_DOWN;
  3367.     ekey[rc].rval    = 11;
  3368.     ekey[rc].name    = "";
  3369.     ekey[rc++].label = "";
  3370.     }
  3371.  
  3372.     ekey[rc].ch = -1;
  3373.  
  3374.     while(1) {
  3375.     /*
  3376.      * Figure out next default value for this context.  The idea
  3377.      * is that in each context the last folder opened is cached.
  3378.      * It's up to pick it out and display it.  This is fine
  3379.      * and dandy if we've currently got the inbox open, BUT
  3380.      * if not, make the inbox the default the first time thru.
  3381.      */
  3382.     if(!inbox){
  3383.         last_folder = ps_global->inbox_name;
  3384.         inbox = 1;        /* pretend we're in inbox from here on out */
  3385.     }
  3386.     else
  3387.       last_folder = (tc->last_folder[0]) ? tc->last_folder : NULL;
  3388.  
  3389.     if(last_folder)
  3390.       sprintf(expanded, " [%s]", last_folder);
  3391.     else
  3392.       *expanded = '\0';
  3393.  
  3394.     /* only show collection number if more than one available */
  3395.     if(ps_global->context_list->next){
  3396.         sprintf(prompt, "GOTO %s in <%.20s> %s%s: ",
  3397.             (tc->type&FTYPE_BBOARD) ? "news group" : "folder",
  3398.             tc->label[0], expanded, *expanded ? " " : "");
  3399.     }
  3400.     else
  3401.       sprintf(prompt, "GOTO folder %s: ", expanded, *expanded ? " " : "");
  3402.  
  3403.         rc = optionally_enter(newfolder, qline, 0, MAXFOLDER, 1 ,0, prompt,
  3404.                               ekey, help, 0);
  3405.  
  3406.     if(rc == -1){
  3407.         q_status_message(SM_ORDER | SM_DING, 3, 3,
  3408.                  "Error reading folder name");
  3409.         return(NULL);
  3410.     }
  3411.     else if(rc == 1){
  3412.         cmd_cancelled("Open Folder");
  3413.         return(NULL);
  3414.     }
  3415.     else if(rc == 2){
  3416.         void (*redraw)() = ps_global->redrawer;
  3417.  
  3418.         push_titlebar_state();
  3419.         rc = folder_lister(ps_global, OpenFolder, tc, &tc, newfolder,
  3420.                    NULL, ps_global->context_list, NULL);
  3421.             ClearScreen();
  3422.         pop_titlebar_state();
  3423.             redraw_titlebar();
  3424.             if(ps_global->redrawer = redraw) /* reset old value, and test */
  3425.               (*ps_global->redrawer)();
  3426.  
  3427.         if(rc == 1 && F_ON(F_SELECT_WO_CONFIRM, ps_global))
  3428.           break;
  3429.     }
  3430.     else if(rc == 3){
  3431.             help = help == NO_HELP ? h_oe_broach : NO_HELP;
  3432.     }
  3433.     else if(rc == 10){            /* Previous collection */
  3434.         tc2 = ps_global->context_list;
  3435.         while(tc2->next && tc2->next != tc)
  3436.           tc2 = tc2->next;
  3437.  
  3438.         tc = tc2;
  3439.     }
  3440.     else if(rc == 11){            /* Next collection */
  3441.         tc = (tc->next) ? tc->next : ps_global->context_list;
  3442.     }
  3443.     else if(rc == 12){            /* file name completion! */
  3444.         if(!folder_complete(tc, newfolder))
  3445.           Writechar(BELL, 0);
  3446.     }
  3447.     else if(rc != 4)
  3448.           break;
  3449.     }
  3450.  
  3451.     removing_trailing_white_space(newfolder);
  3452.     removing_leading_white_space(newfolder);
  3453.  
  3454.     if(*newfolder == '\0'  && last_folder == NULL) {
  3455.     cmd_cancelled("Open folder");
  3456.         return(NULL);
  3457.     }
  3458.  
  3459.     if(*newfolder == '\0')
  3460.       strcpy(newfolder, last_folder);
  3461.  
  3462.     dprint(2, (debugfile, "broach folder, name entered \"%s\"\n",newfolder));
  3463.  
  3464.     /*-- Just check that we can expand this. It gets done for real later --*/
  3465.     strcpy(expanded, newfolder);
  3466.     if (! expand_foldername(expanded)) {
  3467.         dprint(1, (debugfile,
  3468.                     "Error: Failed on expansion of filename %s (save)\n", 
  3469.           expanded));
  3470.         return(NULL);
  3471.     }
  3472.  
  3473.     *context = tc;
  3474.     return(newfolder);
  3475. }
  3476.  
  3477.  
  3478.  
  3479.  
  3480. /*----------------------------------------------------------------------
  3481.     Actually attempt to open given folder 
  3482.  
  3483.   Args: newfolder -- The folder name to open
  3484.  
  3485.  Result:  1 if the folder was successfully opened
  3486.           0 if the folder open failed and went back to old folder
  3487.          -1 if open failed and no folder is left open
  3488.       
  3489.   Attempt to open the folder name given. If the open of the new folder
  3490.   fails then the previously open folder will remain open, unless
  3491.   something really bad has happened. The designate inbox will always be
  3492.   kept open, and when a request to open it is made the already open
  3493.   stream will be used. Making a folder the current folder requires
  3494.   setting the following elements of struct pine: mail_stream, cur_folder,
  3495.   current_msgno, max_msgno. Attempting to reopen the current folder is a 
  3496.   no-op.
  3497.  
  3498.   The first time the inbox folder is opened, usually as Pine starts up,
  3499.   it will be actually opened.
  3500.   ----*/
  3501.  
  3502. do_broach_folder(newfolder, new_context) 
  3503.      char      *newfolder;
  3504.      CONTEXT_S *new_context;
  3505. {
  3506.     MAILSTREAM *m;
  3507.     int         open_inbox, rv, old_tros, we_cancel = 0;
  3508.     char        expanded_file[MAXPATH+1], *old_folder, *old_path, *p;
  3509.     long        openmode;
  3510.     char        status_msg[81];
  3511.     SortOrder    old_sort;
  3512.  
  3513. #if    defined(DOS) && !defined(WIN32)
  3514.     openmode = OP_SHORTCACHE;
  3515. #else
  3516.     openmode = 0L;
  3517. #endif
  3518. #ifdef    DEBUG
  3519.     if(debug > 8)
  3520.       openmode |= OP_DEBUG;
  3521. #endif
  3522.     dprint(1, (debugfile, "About to open folder \"%s\"    inbox: \"%s\"\n",
  3523.            newfolder, ps_global->inbox_name));
  3524.  
  3525.     /*----- Little to do to if reopening same folder -----*/
  3526.     if(new_context == ps_global->context_current && ps_global->mail_stream
  3527.        && strcmp(newfolder, ps_global->cur_folder) == 0){
  3528.     if(ps_global->dead_stream){
  3529.         /* though, if it's not healthy, we reset things and fall thru
  3530.          * to actually reopen it...
  3531.          */
  3532.         q_status_message1(SM_ORDER, 0, 4, 
  3533.                   "Attempting to reopen closed folder \"%s\"",
  3534.                   newfolder);
  3535.         mail_close(ps_global->mail_stream);
  3536.         if(ps_global->mail_stream == ps_global->inbox_stream)
  3537.           ps_global->inbox_stream = NULL;
  3538.  
  3539.         ps_global->mail_stream = NULL;
  3540.         ps_global->expunge_count       = 0;
  3541.         ps_global->new_mail_count      = 0;
  3542.         ps_global->noticed_dead_stream = 0;
  3543.         ps_global->dead_stream         = 0;
  3544.         ps_global->last_msgno_flagged  = 0L;
  3545.         ps_global->mangled_header       = 1;
  3546.         mn_give(&ps_global->msgmap);
  3547.         clear_index_cache();
  3548.         reset_check_point();
  3549.     }
  3550.     else
  3551.       return(1);            /* successful open of same folder! */
  3552.     }
  3553.  
  3554.     /*--- Set flag that we're opening the inbox, a special case ---*/
  3555.     /*
  3556.      * We want to know if inbox is being opened either by name OR
  3557.      * fully qualified path...
  3558.      *
  3559.      * So, IF we're asked to open inbox AND it's already open AND
  3560.      * the only stream AND it's healthy, just return ELSE fall thru
  3561.      * and close mail_stream returning with inbox_stream as new stream...
  3562.      */
  3563.     if(open_inbox = (strucmp(newfolder, ps_global->inbox_name) == 0
  3564.              || strcmp(newfolder, ps_global->VAR_INBOX_PATH) == 0)){
  3565.     new_context = ps_global->context_list; /* restore first context */
  3566.     if(ps_global->inbox_stream 
  3567.        && (ps_global->inbox_stream == ps_global->mail_stream))
  3568.       return(1);
  3569.     }
  3570.  
  3571.     /*
  3572.      * If ambiguous foldername (not fully qualified), make sure it's
  3573.      * not a nickname for a folder in the given context...
  3574.      */
  3575.     strcpy(expanded_file, newfolder);     /* might get reset below */
  3576.     if(!open_inbox && new_context && context_isambig(newfolder)){
  3577.     if (p = folder_is_nick(newfolder, new_context->folders)){
  3578.         strcpy(expanded_file, p);
  3579.         dprint(2, (debugfile, "broach_folder: nickname for %s is %s\n",
  3580.                expanded_file, newfolder));
  3581.     }
  3582.     else if ((new_context->use & CNTXT_INCMNG)
  3583.          && (folder_index(newfolder, new_context->folders) < 0)){
  3584.         q_status_message1(SM_ORDER, 3, 4,
  3585.                 "Can't find Incoming Folder %s.", newfolder);
  3586.         return(0);
  3587.     }
  3588.     }
  3589.  
  3590.     /*--- Opening inbox, inbox has been already opened, the easy case ---*/
  3591.     if(open_inbox && ps_global->inbox_stream != NULL ) {
  3592.         expunge_and_close(ps_global->mail_stream, ps_global->context_current,
  3593.               ps_global->cur_folder, NULL);
  3594.  
  3595.     ps_global->mail_stream              = ps_global->inbox_stream;
  3596.         ps_global->new_mail_count           = 0L;
  3597.         ps_global->expunge_count            = 0L;
  3598.         ps_global->last_msgno_flagged       = 0L;
  3599.         ps_global->mail_box_changed         = 0;
  3600.         ps_global->noticed_dead_stream      = 0;
  3601.         ps_global->noticed_dead_inbox       = 0;
  3602.         ps_global->dead_stream              = 0;
  3603.         ps_global->dead_inbox               = 0;
  3604.     mn_give(&ps_global->msgmap);
  3605.     ps_global->msgmap            = ps_global->inbox_msgmap;
  3606.     ps_global->inbox_msgmap            = NULL;
  3607.  
  3608.     dprint(7, (debugfile, "%ld %ld %x\n",
  3609.            mn_get_cur(ps_global->msgmap),
  3610.                    mn_get_total(ps_global->msgmap),
  3611.            ps_global->mail_stream));
  3612.     /*
  3613.      * remember last context and folder
  3614.      */
  3615.     if(context_isambig(ps_global->cur_folder)){
  3616.         ps_global->context_last = ps_global->context_current;
  3617.         strcpy(ps_global->context_current->last_folder,
  3618.          ps_global->cur_folder);
  3619.     }
  3620.  
  3621.     strcpy(ps_global->cur_folder, ps_global->inbox_name);
  3622.     ps_global->context_current = ps_global->context_list;
  3623.         clear_index_cache();
  3624.         /* MUST sort before restoring msgno! */
  3625.     sort_current_folder(1, mn_get_sort(ps_global->msgmap),
  3626.                 mn_get_revsort(ps_global->msgmap));
  3627.         q_status_message2(SM_ORDER, 0, 3,
  3628.               "Opened folder \"INBOX\" with %s message%s",
  3629.                           long2string(mn_get_total(ps_global->msgmap)),
  3630.                           mn_get_total(ps_global->msgmap) != 1 ? "s" : "" );
  3631.     return(1);
  3632.     }
  3633.  
  3634.     if(!new_context && ! expand_foldername(expanded_file))
  3635.       return(0);
  3636.  
  3637.     old_folder = NULL;
  3638.     old_path   = NULL;
  3639.     old_sort   = SortArrival;            /* old sort */
  3640.     old_tros   = 0;                /* old reverse sort ? */
  3641.     /*---- now close the old one we had open if there was one ----*/
  3642.     if(ps_global->mail_stream != NULL){
  3643.         old_folder   = cpystr(ps_global->cur_folder);
  3644.         old_path     = cpystr(ps_global->mail_stream->mailbox);
  3645.     old_sort     = mn_get_sort(ps_global->msgmap);
  3646.     old_tros     = mn_get_revsort(ps_global->msgmap);
  3647.     if(strcmp(ps_global->cur_folder, ps_global->inbox_name) == 0){
  3648.         /*-- don't close the inbox stream, save a bit of state --*/
  3649.         if(ps_global->inbox_msgmap)
  3650.           mn_give(&ps_global->inbox_msgmap);
  3651.  
  3652.         ps_global->inbox_msgmap = ps_global->msgmap;
  3653.         ps_global->msgmap       = NULL;
  3654.  
  3655.         dprint(2, (debugfile,
  3656.                "Close - saved inbox state: max %ld\n",
  3657.                mn_get_total(ps_global->inbox_msgmap)));
  3658.     }
  3659.     else
  3660.           expunge_and_close(ps_global->mail_stream, ps_global->context_current,
  3661.                 ps_global->cur_folder, NULL);
  3662.     }
  3663.  
  3664.     strcat(strncat(strcpy(status_msg, "Opening \""),
  3665.         pretty_fn(newfolder), 70), "\"");
  3666.     we_cancel = busy_alarm(1, status_msg, NULL, 1);
  3667.  
  3668.     /* 
  3669.      * if requested, make access to folder readonly (only once)
  3670.      */
  3671.     if (ps_global->open_readonly_on_startup) {
  3672.     openmode |= OP_READONLY ;
  3673.     ps_global->open_readonly_on_startup = 0 ;
  3674.     }
  3675.  
  3676.     /*
  3677.      * The name "inbox" is special, so treat it so 
  3678.      * (used to by handled by expand_folder)...
  3679.      */
  3680.     if(ps_global->nr_mode)
  3681.       ps_global->noshow_warn = 1;
  3682.  
  3683.     m = context_open((new_context && !open_inbox) ? new_context->context:NULL,
  3684.              NULL, 
  3685.              (open_inbox) ? ps_global->VAR_INBOX_PATH : expanded_file,
  3686.              openmode);
  3687.  
  3688.     if(ps_global->nr_mode)
  3689.       ps_global->noshow_warn = 0;
  3690.  
  3691.     dprint(8, (debugfile, "Opened folder %p \"%s\" (context: \"%s\")\n",
  3692.                m, (m) ? m->mailbox : "nil",
  3693.            (new_context) ? new_context->context : "nil"));
  3694.  
  3695.  
  3696.     /* Can get m != NULL if correct passwd for remote, but wrong name */
  3697.     if(m == NULL || ((p = strindex(m->mailbox, '<')) != NULL &&
  3698.                       strcmp(p + 1, "no_mailbox>") == 0)) {
  3699.     /*-- non-existent local mailbox, or wrong passwd for remote mailbox--*/
  3700.         /* fall back to currently open mailbox */
  3701.     if(we_cancel)
  3702.       cancel_busy_alarm(-1);
  3703.  
  3704.         rv = 0;
  3705.         dprint(8, (debugfile, "Old folder: \"%s\"\n",
  3706.                old_folder == NULL ? "" : old_folder));
  3707.         if(old_folder != NULL) {
  3708.             if(strcmp(old_folder, ps_global->inbox_name) == 0){
  3709.                 ps_global->mail_stream = ps_global->inbox_stream;
  3710.         if(ps_global->msgmap)
  3711.           mn_give(&ps_global->msgmap);
  3712.  
  3713.         ps_global->msgmap       = ps_global->inbox_msgmap;
  3714.         ps_global->inbox_msgmap = NULL;
  3715.  
  3716.                 dprint(8, (debugfile, "Reactivate inbox %ld %ld %p\n",
  3717.                            mn_get_cur(ps_global->msgmap),
  3718.                            mn_get_total(ps_global->msgmap),
  3719.                            ps_global->mail_stream));
  3720.                 strcpy(ps_global->cur_folder, ps_global->inbox_name);
  3721.             } else {
  3722.                 ps_global->mail_stream = mail_open(NULL, old_path, openmode);
  3723.                 /* mm_log will take care of error message here */
  3724.                 if(ps_global->mail_stream == NULL) {
  3725.                     rv = -1;
  3726.                 } else {
  3727.             mn_init(&(ps_global->msgmap),
  3728.                 ps_global->mail_stream->nmsgs);
  3729.             mn_set_sort(ps_global->msgmap, old_sort);
  3730.             mn_set_revsort(ps_global->msgmap, old_tros);
  3731.                     ps_global->expunge_count       = 0;
  3732.                     ps_global->new_mail_count      = 0;
  3733.                     ps_global->noticed_dead_stream = 0;
  3734.                     ps_global->dead_stream         = 0;
  3735.             ps_global->last_msgno_flagged  = 0L;
  3736.             ps_global->mangled_header       = 1;
  3737.  
  3738.             clear_index_cache();
  3739.                     reset_check_point();
  3740.             if(mn_get_total(ps_global->msgmap) > 0)
  3741.               mn_set_cur(ps_global->msgmap, 1L);
  3742.  
  3743.             if(mn_get_sort(ps_global->msgmap) != SortArrival
  3744.                || mn_get_revsort(ps_global->msgmap))
  3745.               sort_current_folder(1, mn_get_sort(ps_global->msgmap),
  3746.                       mn_get_revsort(ps_global->msgmap));
  3747.  
  3748.                     q_status_message1(SM_ORDER, 0, 3, "Folder \"%s\" reopened",
  3749.                                       old_folder);
  3750.                 }
  3751.             }
  3752.  
  3753.         if(rv == 0)
  3754.           mn_set_cur(ps_global->msgmap,
  3755.              min(mn_get_cur(ps_global->msgmap), 
  3756.                  mn_get_total(ps_global->msgmap)));
  3757.  
  3758.             fs_give((void **)&old_folder);
  3759.             fs_give((void **)&old_path);
  3760.         } else {
  3761.             rv = -1;
  3762.         }
  3763.         if(rv == -1) {
  3764.             q_status_message(SM_ORDER | SM_DING, 0, 4, "No folder opened");
  3765.         mn_set_total(ps_global->msgmap, 0L);
  3766.         mn_set_cur(ps_global->msgmap, -1L);
  3767.             strcpy(ps_global->cur_folder, "");
  3768.         }
  3769.         return(rv);
  3770.     } else {
  3771.         if(old_folder != NULL) {
  3772.             fs_give((void **)&old_folder);
  3773.             fs_give((void **)&old_path);
  3774.         }
  3775.     }
  3776.  
  3777.     /*----- success in opening the new folder ----*/
  3778.     dprint(2, (debugfile, "Opened folder \"%s\" with %ld messages\n",
  3779.            m->mailbox, m->nmsgs));
  3780.  
  3781.  
  3782.     /*--- A Little house keeping ---*/
  3783.     ps_global->mail_stream          = m;
  3784.     ps_global->expunge_count        = 0L;
  3785.     ps_global->new_mail_count       = 0L;
  3786.     ps_global->last_msgno_flagged   = 0L;
  3787.     ps_global->noticed_dead_stream  = 0;
  3788.     ps_global->noticed_dead_inbox   = 0;
  3789.     ps_global->dead_stream          = 0;
  3790.     ps_global->dead_inbox           = 0;
  3791.     mn_init(&(ps_global->msgmap), m->nmsgs);
  3792.  
  3793.     /*
  3794.      * remember old folder and context...
  3795.      */
  3796.     if(context_isambig(ps_global->cur_folder)
  3797.        || strucmp(ps_global->cur_folder, ps_global->inbox_name) == 0)
  3798.       strcpy(ps_global->context_current->last_folder,ps_global->cur_folder);
  3799.  
  3800.     strcpy(ps_global->cur_folder, (open_inbox) ? ps_global->inbox_name
  3801.                            : newfolder);
  3802.     if(new_context){
  3803.     ps_global->context_last    = ps_global->context_current;
  3804.     ps_global->context_current = new_context;
  3805.     }
  3806.  
  3807.     reset_check_point();
  3808.     clear_index_cache();
  3809.     ps_global->mail_box_changed = 0;
  3810.  
  3811.     /*
  3812.      * Start news reading with messages the user's marked deleted
  3813.      * hidden from view...
  3814.      */
  3815.     if(IS_NEWS(ps_global->mail_stream) && ps_global->mail_stream->rdonly)
  3816.       msgno_exclude(ps_global->mail_stream, ps_global->msgmap);
  3817.  
  3818.     if(we_cancel)
  3819.       cancel_busy_alarm(0);
  3820.  
  3821.     /* UWIN doesn't want to see this message */
  3822.     if(!ps_global->nr_mode)
  3823.       q_status_message7(SM_ORDER, 0, 4, "%s \"%s\" opened with %s message%s%s",
  3824.             IS_NEWS(ps_global->mail_stream)
  3825.               ? "News group" : "Folder",
  3826.             pretty_fn(newfolder),
  3827.             comatose(mn_get_total(ps_global->msgmap)),
  3828.             plural(mn_get_total(ps_global->msgmap)),
  3829.             READONLY_FOLDER ?" READONLY" : "",
  3830.             NULL, NULL);
  3831.  
  3832.     sort_current_folder(0, SortArrival, 0);
  3833.  
  3834.     if(mn_get_total(ps_global->msgmap) > 0L) {
  3835.     if(ps_global->start_entry > 0) {
  3836.         mn_set_cur(ps_global->msgmap,
  3837.                min(ps_global->start_entry,
  3838.                mn_get_total(ps_global->msgmap)));
  3839.         ps_global->start_entry = 0;
  3840.         }
  3841.     /* if faking new, goto first recent, unseen */
  3842.     else if(IS_NEWS(ps_global->mail_stream)
  3843.         && F_ON(F_FAKE_NEW_IN_NEWS, ps_global)){
  3844.         mn_set_cur(ps_global->msgmap,
  3845.                first_sorted_flagged(F_RECENT|F_UNDEL,m));
  3846.     }
  3847.     /* if it is an incoming mailbox, goto first unseen */
  3848.     else if(open_inbox || (ps_global->context_current->use&CNTXT_INCMNG)
  3849.           || IS_NEWS(ps_global->mail_stream)){
  3850.         mn_set_cur(ps_global->msgmap,
  3851.                first_sorted_flagged(F_UNSEEN|F_UNDEL,m));
  3852.     }
  3853.         else{
  3854.         mn_set_cur(ps_global->msgmap,
  3855.                mn_get_revsort(ps_global->msgmap)
  3856.                  ? 1L
  3857.              : mn_get_total(ps_global->msgmap));
  3858.     }
  3859.     }
  3860.     else{
  3861.     mn_set_cur(ps_global->msgmap, -1L);
  3862.     }
  3863.  
  3864.     /*--- If we just opened the inbox remember it's special stream ---*/
  3865.     if(open_inbox && ps_global->inbox_stream == NULL)
  3866.       ps_global->inbox_stream = ps_global->mail_stream;
  3867.     
  3868.     return(1);
  3869. }
  3870.  
  3871.  
  3872.  
  3873. /*----------------------------------------------------------------------
  3874.     Open the requested folder in the requested context
  3875.  
  3876.     Args: state -- usual pine state struct
  3877.       newfolder -- folder to open
  3878.       new_context -- folder context might live in
  3879.  
  3880.    Result: New folder open or not (if error), and we're set to
  3881.        enter the index screen.
  3882.  ----*/
  3883. void
  3884. visit_folder(state, newfolder, new_context)
  3885.     struct pine *state;
  3886.     char    *newfolder;
  3887.     CONTEXT_S    *new_context;
  3888. {
  3889.     dprint(9, (debugfile, "do_broach_folder (%s, %s)\n",
  3890.            newfolder, new_context->context));
  3891.     if(do_broach_folder(newfolder, new_context) > 0
  3892.        || state->mail_stream != state->inbox_stream)
  3893.       state->next_screen = mail_index_screen;
  3894. }
  3895.  
  3896.  
  3897.  
  3898. /*----------------------------------------------------------------------
  3899.       Expunge (if confirmed) and close a mail stream
  3900.  
  3901.     Args: stream   -- The MAILSTREAM * to close
  3902.       context  -- The contest to interpret the following name in
  3903.           folder   -- The name the folder is know by 
  3904.         final_msg  -- If non-null, this should be set to point to a
  3905.               message to print out in the caller, it is allocated
  3906.               here and freed by the caller.
  3907.  
  3908.    Result:  Mail box is expunged and closed. A message is displayed to
  3909.              say what happened
  3910.  ----*/
  3911. void
  3912. expunge_and_close(stream, context, folder, final_msg)
  3913.     MAILSTREAM *stream;
  3914.     CONTEXT_S  *context;
  3915.     char       *folder;
  3916.     char      **final_msg;
  3917. {
  3918.     long  delete_count, max_folder, seen_not_del;
  3919.     char  prompt_b[MAX_SCREEN_COLS+1], short_folder_name[MAXFOLDER*2+1],
  3920.           temp[MAXFOLDER*2+1], buff1[MAX_SCREEN_COLS+1], *moved_msg = NULL,
  3921.       buff2[MAX_SCREEN_COLS+1];
  3922.     struct variable *vars = ps_global->vars;
  3923.     int ret;
  3924.     char ing[4];
  3925.  
  3926.     if(final_msg)
  3927.       strcpy(ing, "ed");
  3928.     else
  3929.       strcpy(ing, "ing");
  3930.  
  3931.     buff1[0] = '\0';
  3932.     buff2[0] = '\0';
  3933.     if(stream != NULL){
  3934.         dprint(2, (debugfile, "expunge and close mail stream \"%s\"\n",
  3935.                    stream->mailbox));
  3936.         if(!stream->rdonly){
  3937.  
  3938.             q_status_message1(SM_INFO, 0, 1, "Closing \"%s\"...", folder);
  3939.         flush_status_messages(1);
  3940.  
  3941.         /* Save read messages? */
  3942.         if(VAR_READ_MESSAGE_FOLDER && VAR_READ_MESSAGE_FOLDER[0]
  3943.            && stream == ps_global->inbox_stream
  3944.            && (seen_not_del = count_flagged(stream, "SEEN UNDELETED"))){
  3945.  
  3946.         if(F_ON(F_AUTO_READ_MSGS,ps_global)
  3947.            || read_msg_prompt(seen_not_del, VAR_READ_MESSAGE_FOLDER))
  3948.           /* move inbox's read messages */
  3949.           moved_msg = move_read_msgs(stream, VAR_READ_MESSAGE_FOLDER,
  3950.                          buff1, -1L);
  3951.         }
  3952.         else if(VAR_ARCHIVED_FOLDERS)
  3953.           moved_msg = move_read_incoming(stream, context, folder,
  3954.                          VAR_ARCHIVED_FOLDERS,
  3955.                          buff1);
  3956.  
  3957.             delete_count = count_flagged(stream, "DELETED");
  3958.         ret = 'n';
  3959.         if(delete_count){
  3960.         max_folder = ps_global->ttyo->screen_cols - 50;
  3961.         strcpy(temp, pretty_fn(folder));
  3962.         if(strlen(temp) > max_folder){
  3963.             strcpy(short_folder_name, "...");
  3964.             strcat(short_folder_name,temp + strlen(temp) -max_folder);
  3965.         }
  3966.         else
  3967.           strcpy(short_folder_name, temp);
  3968.                 
  3969.         if(F_OFF(F_AUTO_EXPUNGE,ps_global)){
  3970.             sprintf(prompt_b,
  3971.                 "Expunge the %ld deleted message%s from \"%s\"",
  3972.                 delete_count,
  3973.                 delete_count == 1 ? "" : "s",
  3974.                 short_folder_name);
  3975.             ret = want_to(prompt_b, 'y', 0, NO_HELP, 0, 0);
  3976.         }
  3977.         else
  3978.           ret = 'y';
  3979.  
  3980.         /* get this message back in queue */
  3981.         if(moved_msg)
  3982.           q_status_message(SM_ORDER,
  3983.               F_ON(F_AUTO_READ_MSGS,ps_global) ? 0 : 3, 5, moved_msg);
  3984.  
  3985.         if(ret == 'y'){
  3986.             sprintf(buff2,
  3987.               "Clos%s \"%.30s\". %s %s message%s and delet%s %s.",
  3988.             ing,
  3989.              pretty_fn(folder),
  3990.             final_msg ? "Kept" : "Keeping",
  3991.             comatose((stream->nmsgs - delete_count)),
  3992.             plural(stream->nmsgs - delete_count),
  3993.             ing,
  3994.             long2string(delete_count));
  3995.             if(final_msg)
  3996.               *final_msg = cpystr(buff2);
  3997.             else
  3998.               q_status_message(SM_ORDER,
  3999.                        F_ON(F_AUTO_EXPUNGE,ps_global) ? 0 :3,
  4000.                        5, buff2);
  4001.               
  4002.             flush_status_messages(1);
  4003.             ps_global->mm_log_error = 0;
  4004.             ps_global->expunge_in_progress = 1;
  4005.             mail_expunge(stream);
  4006.             ps_global->expunge_in_progress = 0;
  4007.             if(ps_global->mm_log_error && final_msg && *final_msg){
  4008.             fs_give((void **)final_msg);
  4009.             *final_msg = NULL;
  4010.             }
  4011.         }
  4012.         }
  4013.  
  4014.         if(ret != 'y'){
  4015.         if(stream->nmsgs){
  4016.             sprintf(buff2,
  4017.                 "Clos%s folder \"%s\". %s%s%s message%s.",
  4018.             ing,
  4019.             pretty_fn(folder), 
  4020.             final_msg ? "Kept" : "Keeping",
  4021.             (stream->nmsgs == 1L) ? " single" : " all ",
  4022.             (stream->nmsgs > 1L)
  4023.               ? comatose(stream->nmsgs) : "",
  4024.             plural(stream->nmsgs));
  4025.         }
  4026.         else{
  4027.             sprintf(buff2, "Clos%s empty folder \"%s\"",
  4028.             ing, pretty_fn(folder));
  4029.         }
  4030.  
  4031.         if(final_msg)
  4032.           *final_msg = cpystr(buff2);
  4033.         else
  4034.           q_status_message(SM_ORDER, 0, 3, buff2);
  4035.         }
  4036.         }
  4037.     else{
  4038.             if(IS_NEWS(stream)){
  4039.         /* first, look to archive read messages */
  4040.         if(moved_msg = move_read_incoming(stream, context, folder,
  4041.                           VAR_ARCHIVED_FOLDERS,
  4042.                           buff1))
  4043.           q_status_message(SM_ORDER,
  4044.               F_ON(F_AUTO_READ_MSGS,ps_global) ? 0 : 3, 5, moved_msg);
  4045.  
  4046.         sprintf(buff2, "Clos%s news group \"%s\"",
  4047.             ing, pretty_fn(folder));
  4048.         }
  4049.             else
  4050.           sprintf(buff2,
  4051.             "Clos%s read-only folder \"%s\". No changes to save",
  4052.             ing, pretty_fn(folder));
  4053.  
  4054.         if(final_msg)
  4055.           *final_msg = cpystr(buff2);
  4056.         else
  4057.           q_status_message(SM_ORDER, 0, 2, buff2);
  4058.         }
  4059.  
  4060.     /*
  4061.      * Make darn sure any mm_log fallout caused above get's seen...
  4062.      */
  4063.     flush_status_messages(1);
  4064.     }
  4065.  
  4066.     mail_close(stream);
  4067. }
  4068.  
  4069.  
  4070.  
  4071. /*----------------------------------------------------------------------
  4072.   Move all read messages from srcfldr to dstfldr
  4073.  
  4074.   Args: stream -- stream to usr
  4075.     dstfldr -- folder to receive moved messages
  4076.     buf -- place to write success message
  4077.  
  4078.   Returns: success message or NULL for failure
  4079.   ----*/
  4080. char *
  4081. move_read_msgs(stream, dstfldr, buf, searched)
  4082.     MAILSTREAM *stream;
  4083.     char       *dstfldr, *buf;
  4084.     long    searched;
  4085. {
  4086.     long      i;
  4087.     int           we_cancel = 0;
  4088.     MSGNO_S     *msgmap = NULL;
  4089.     CONTEXT_S     *save_context;
  4090.     char      *bufp = NULL;
  4091.  
  4092.     save_context = default_save_context(ps_global->context_list);
  4093.     if(!save_context)
  4094.       save_context = ps_global->context_list;
  4095.  
  4096.     /*
  4097.      * Use the "sequence" bit to select the set of messages
  4098.      * we want to save.  If searched is non-neg, the message
  4099.      * cache already has the necessary "sequence" bits set.
  4100.      */
  4101.     if(searched < 0L)
  4102.       searched = count_flagged(stream, "SEEN UNDELETED");
  4103.  
  4104.     if(searched){
  4105.     mn_init(&msgmap, stream->nmsgs);
  4106.     for(i = 1L; i <= mn_get_total(msgmap); i++)
  4107.       set_lflag(stream, msgmap, i, MN_SLCT, 0);
  4108.  
  4109.     /*
  4110.      * re-init msgmap to fix the MN_SLCT count, "flagged_tmp", in
  4111.      * case there were any flagged such before we got here.
  4112.      *
  4113.      * BUG: this means the count of MN_SLCT'd msgs in the
  4114.      * folder's real msgmap is instantly bogus.  Until Cancel
  4115.      * after "Really quit?" is allowed, this isn't a problem since
  4116.      * that mapping table is either gone or about to get nuked...
  4117.      */
  4118.     mn_init(&msgmap, stream->nmsgs);
  4119.  
  4120.     /* select search results */
  4121.     for(i = 1L; i <= mn_get_total(msgmap); i++)
  4122.       if(mail_elt(stream, mn_m2raw(msgmap, i))->searched)
  4123.         set_lflag(stream, msgmap, i, MN_SLCT, 1);
  4124.  
  4125.     pseudo_selected(msgmap);
  4126.     sprintf(buf, "Moving %s read message%s to \"%.45s\"",
  4127.         comatose(searched), plural(searched), dstfldr);
  4128.     we_cancel = busy_alarm(1, buf, NULL, 1);
  4129.     if(save(ps_global, save_context, dstfldr, msgmap, 1) != searched)
  4130.       q_status_message1(SM_ORDER | SM_DING, 4, 6,
  4131.                 "Error saving to %.35s.  Not all messages moved.",
  4132.                 dstfldr);
  4133.     else
  4134.       strncpy(bufp = buf + 1, "Moved", 5); /* change Moving to Moved */
  4135.  
  4136.     mn_give(&msgmap);
  4137.     if(we_cancel)
  4138.       cancel_busy_alarm(bufp ? 0 : -1);
  4139.     }
  4140.  
  4141.     return(bufp);
  4142. }
  4143.  
  4144.  
  4145.  
  4146. /*----------------------------------------------------------------------
  4147.   Move read messages from folder if listed in archive
  4148.  
  4149.   Args: 
  4150.  
  4151.   ----*/
  4152. int
  4153. read_msg_prompt(n, f)
  4154.     long  n;
  4155.     char *f;
  4156. {
  4157.     char buf[MAX_SCREEN_COLS+1];
  4158.  
  4159.     sprintf(buf, "Save the %ld read message%s in \"%s\"", n, plural(n), f);
  4160.     return(want_to(buf, 'y', 0, NO_HELP, 0, 0) == 'y');
  4161. }
  4162.  
  4163.  
  4164.  
  4165. /*----------------------------------------------------------------------
  4166.   Move read messages from folder if listed in archive
  4167.  
  4168.   Args: 
  4169.  
  4170.   ----*/
  4171. char *
  4172. move_read_incoming(stream, context, folder, archive, buf)
  4173.     MAILSTREAM *stream;
  4174.     CONTEXT_S  *context;
  4175.     char       *folder;
  4176.     char      **archive;
  4177.     char       *buf;
  4178. {
  4179.     char *s, *d, *f = folder;
  4180.     long  seen_undel;
  4181.  
  4182.     buf[0] = '\0';
  4183.  
  4184.     if(archive && stream != ps_global->inbox_stream
  4185.        && (context && CNTXT_INCMNG)
  4186.        && ((context_isambig(folder)
  4187.         && folder_is_nick(folder, context->folders))
  4188.        || folder_index(folder, context->folders) > 0)
  4189.        && (seen_undel = count_flagged(stream, "SEEN UNDELETED"))){
  4190.  
  4191.     for(; f && *archive; archive++){
  4192.         get_pair(*archive, &s, &d, 1);
  4193.         if(s && d && !strcmp(s, folder)){
  4194.         if(F_ON(F_AUTO_READ_MSGS,ps_global)
  4195.            || read_msg_prompt(seen_undel, d))
  4196.           buf = move_read_msgs(stream, d, buf, seen_undel);
  4197.  
  4198.         f = NULL;        /* bust out after cleaning up */
  4199.         }
  4200.  
  4201.         fs_give((void **)&s);
  4202.         fs_give((void **)&d);
  4203.     }
  4204.     }
  4205.  
  4206.     return((buf && *buf) ? buf : NULL);
  4207. }
  4208.  
  4209.  
  4210.  
  4211. /*----------------------------------------------------------------------
  4212.       Search the message headers as display in index
  4213.  
  4214.   Args: command_line -- The screen line to prompt on
  4215.         msg          -- The current message number to start searching at
  4216.         max_msg      -- The largest message number in the current folder
  4217.  
  4218.   The headers are searched exactly as they are displayed on the screen. The
  4219. search will wrap around to the beginning if not string is not found right 
  4220. away.
  4221.   ----*/
  4222. void
  4223. search_headers(state, stream, command_line, msgmap)
  4224.     struct pine *state;
  4225.     MAILSTREAM  *stream;
  4226.     int          command_line;
  4227.     MSGNO_S     *msgmap;
  4228. {
  4229.     int         rc, select_all = 0;
  4230.     long        i, sorted_msg, selected = 0L;
  4231.     char        prompt[MAX_SEARCH+50], new_string[MAX_SEARCH+1];
  4232.     HelpType    help;
  4233.     static char search_string[MAX_SEARCH+1] = { '\0' };
  4234.     static ESCKEY_S header_search_key[] = { {0, 0, NULL, NULL },
  4235.                         {ctrl('Y'), 10, "^Y", "First Msg"},
  4236.                         {ctrl('V'), 11, "^V", "Last Msg"},
  4237.                         {-1, 0, NULL, NULL} };
  4238.  
  4239.     dprint(4, (debugfile, "\n - search headers - \n"));
  4240.  
  4241.     if(!any_messages(msgmap, NULL, "to search")){
  4242.     return;
  4243.     }
  4244.     else if(mn_total_cur(msgmap) > 1L){
  4245.     q_status_message1(SM_ORDER, 0, 2, "%s msgs selected; Can't search",
  4246.               comatose(mn_total_cur(msgmap)));
  4247.     return;
  4248.     }
  4249.     else
  4250.       sorted_msg = mn_get_cur(msgmap);
  4251.  
  4252.     help = NO_HELP;
  4253.     new_string[0] = '\0';
  4254.  
  4255.     while(1) {
  4256.     sprintf(prompt, "Word to search for [%s] : ", search_string);
  4257.  
  4258.     if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
  4259.         header_search_key[0].ch    = ctrl('X');
  4260.         header_search_key[0].rval  = 12;
  4261.         header_search_key[0].name  = "^X";
  4262.         header_search_key[0].label = "Select Matches";
  4263.     }
  4264.     else{
  4265.         header_search_key[0].ch   = header_search_key[0].rval  = 0;
  4266.         header_search_key[0].name = header_search_key[0].label = NULL;
  4267.     }
  4268.     
  4269.         rc = optionally_enter(new_string, command_line, 0, MAX_SEARCH, 1,
  4270.                   0, prompt, header_search_key, help, 0);
  4271.  
  4272.         if(rc == 3) {
  4273.         help = (help != NO_HELP) ? NO_HELP :
  4274.              F_ON(F_ENABLE_AGG_OPS, ps_global) ? h_os_index_whereis_agg
  4275.                : h_os_index_whereis;
  4276.             continue;
  4277.         }
  4278.     else if(rc == 10){
  4279.         q_status_message(SM_ORDER, 0, 3, "Searched to First Message.");
  4280.         if(any_lflagged(msgmap, MN_HIDE)){
  4281.         do{
  4282.             selected = sorted_msg;
  4283.             mn_dec_cur(stream, msgmap);
  4284.             sorted_msg = mn_get_cur(msgmap);
  4285.         }
  4286.         while(selected != sorted_msg);
  4287.         }
  4288.         else
  4289.           sorted_msg = (mn_get_total(msgmap) > 0L) ? 1L : 0L;
  4290.  
  4291.         mn_set_cur(msgmap, sorted_msg);
  4292.         return;
  4293.     }
  4294.     else if(rc == 11){
  4295.         q_status_message(SM_ORDER, 0, 3, "Searched to Last Message.");
  4296.         if(any_lflagged(msgmap, MN_HIDE)){
  4297.         do{
  4298.             selected = sorted_msg;
  4299.             mn_inc_cur(stream, msgmap);
  4300.             sorted_msg = mn_get_cur(msgmap);
  4301.         }
  4302.         while(selected != sorted_msg);
  4303.         }
  4304.         else
  4305.           sorted_msg = mn_get_total(msgmap);
  4306.  
  4307.         mn_set_cur(msgmap, sorted_msg);
  4308.         return;
  4309.     }
  4310.     else if(rc == 12){
  4311.         select_all = 1;
  4312.         break;
  4313.     }
  4314.  
  4315.         if(rc != 4)            /* redraw */
  4316.           break; /* redraw */
  4317.     }
  4318.  
  4319.     if(rc == 1 || (new_string[0] == '\0' && search_string[0] == '\0')) {
  4320.     cmd_cancelled("Search");
  4321.         return;
  4322.     }
  4323.  
  4324.     if(new_string[0] == '\0')
  4325.       strcpy(new_string, search_string);
  4326.  
  4327.     strcpy(search_string, new_string);
  4328.  
  4329.     for(i = sorted_msg + ((select_all)?0:1); i <= mn_get_total(msgmap); i++) {
  4330.         if(!get_lflag(stream, msgmap, i, MN_HIDE) &&
  4331.        srchstr(build_header_line(state, stream,
  4332.                msgmap, i)->line, search_string)){
  4333.         if(select_all){
  4334.         set_lflag(stream, msgmap, i, MN_SLCT, 1);
  4335.         selected++;
  4336.         }
  4337.         else
  4338.           goto found;
  4339.     }
  4340.     }
  4341.  
  4342.     for(i = 1; i < sorted_msg; i++) {
  4343.         if(!get_lflag(stream, msgmap, i, MN_HIDE) &&
  4344.        srchstr(build_header_line(state, stream,
  4345.                msgmap, i)->line, search_string)){
  4346.         if(select_all){
  4347.         set_lflag(stream, msgmap, i, MN_SLCT, 1);
  4348.         selected++;
  4349.         }
  4350.         else
  4351.           goto found;
  4352.     }
  4353.     }
  4354.  
  4355.     if(!select_all){
  4356.     q_status_message(SM_ORDER, 0, 3, "Word not found");
  4357.     return;
  4358.   found:
  4359.     q_status_message1(SM_ORDER, 0, 3, "Word found%s", i > sorted_msg ? "" :
  4360.               ". Search wrapped to beginning"); 
  4361.     mn_set_cur(msgmap, i);
  4362.     }
  4363.     else{
  4364.     q_status_message1(SM_ORDER, 0, 3, "%s messages found matching word",
  4365.               long2string(selected));
  4366.     }
  4367. }
  4368.  
  4369.  
  4370.  
  4371. /*----------------------------------------------------------------------
  4372.     Print current message[s] or folder index
  4373.  
  4374.  Filters the original header and sends stuff to printer
  4375.   ---*/
  4376. void
  4377. cmd_print(state, msgmap, agg, in_index)
  4378.      struct pine *state;
  4379.      MSGNO_S     *msgmap;
  4380.      int      agg, in_index;
  4381. {
  4382.     char      prompt[250];
  4383.     long      i, msgs;
  4384.     int          next = 0, print_index = 0;
  4385.     ENVELOPE *e;
  4386.     BODY     *b;
  4387.  
  4388.     if(agg && !pseudo_selected(msgmap))
  4389.       return;
  4390.  
  4391.     msgs = mn_total_cur(msgmap);
  4392.  
  4393.     if(in_index && F_ON(F_PRINT_INDEX, state)){
  4394.     char m[10];
  4395.     static ESCKEY_S prt_opts[] = {
  4396.         {'i', 'i', "I", "Index"},
  4397.         {'m', 'm', "M", NULL},
  4398.         {-1, 0, NULL, NULL}};
  4399.  
  4400.     sprintf(m, "Message%s", (msgs>1L) ? "s" : "");
  4401.     prt_opts[1].label = m;
  4402.     sprintf(prompt, "Print %sFolder Index or %s %s? ",
  4403.         agg ? "selected " : "", agg ? "selected" : "current", m);
  4404.     switch(radio_buttons(prompt, -FOOTER_ROWS(state), prt_opts, 'm', 'x',
  4405.                  NO_HELP, RB_NORM)){
  4406.       case 'x' :
  4407.         cmd_cancelled("Print");
  4408.         if(agg)
  4409.           restore_selected(msgmap);
  4410.  
  4411.         return;
  4412.  
  4413.       case 'i':
  4414.         print_index = 1;
  4415.         break;
  4416.  
  4417.       default :
  4418.       case 'm':
  4419.         break;
  4420.     }
  4421.     }
  4422.  
  4423.     if(print_index)
  4424.       sprintf(prompt, "%sFolder Index ", agg ? "Selected " : "");
  4425.     else if(msgs > 1L)
  4426.       sprintf(prompt, "%s messages ", long2string(msgs));
  4427.     else
  4428.       sprintf(prompt, "Message %s ", long2string(mn_get_cur(msgmap)));
  4429.  
  4430.     if(open_printer(prompt) < 0){
  4431.     if(agg)
  4432.       restore_selected(msgmap);
  4433.  
  4434.     return;
  4435.     }
  4436.     
  4437.     if(print_index){
  4438.     /*
  4439.      * Print titlebar, then all the index members...
  4440.      */
  4441.     print_text1("%s\n\n", format_titlebar(NULL));
  4442.  
  4443.     for(i = 1L; i <= mn_get_total(msgmap); i++){
  4444.         if(agg && !get_lflag(state->mail_stream, msgmap, i, MN_SLCT))
  4445.           continue;
  4446.  
  4447.         if(!print_char((mn_is_cur(msgmap, i)) ? '>' : ' ')
  4448.            || !gf_puts(build_header_line(state, state->mail_stream,
  4449.                          msgmap, i)->line + 1, print_char)
  4450.            || !gf_puts(NEWLINE, print_char)){
  4451.         q_status_message(SM_ORDER | SM_DING, 3, 3,
  4452.                  "Error printing folder index");
  4453.         break;
  4454.         }
  4455.     }
  4456.     }
  4457.     else{
  4458.         for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap), next++){
  4459.         if(next && F_ON(F_AGG_PRINT_FF, state))
  4460.           if(!print_char(FORMFEED))
  4461.             break;
  4462.  
  4463.         if(!(e=mail_fetchstructure(state->mail_stream, mn_m2raw(msgmap,i),
  4464.                        &b))
  4465.            || (F_ON(F_FROM_DELIM_IN_PRINT, ps_global)
  4466.            && !bezerk_delimiter(e, print_char, next))
  4467.            || !format_message(mn_m2raw(msgmap, mn_get_cur(msgmap)), e, b,
  4468.                 (FM_NEW_MESS|FM_DO_PRINT), print_char)){
  4469.             q_status_message(SM_ORDER | SM_DING, 3, 3,
  4470.                    "Error printing message");
  4471.             break;
  4472.         }
  4473.         }
  4474.     }
  4475.  
  4476.     close_printer();
  4477.  
  4478.     if(agg)
  4479.       restore_selected(msgmap);
  4480. }
  4481.  
  4482.  
  4483.  
  4484. /*
  4485.  * Support structure and functions to support piping raw message texts...
  4486.  */
  4487. static struct raw_pipe_data {
  4488.     MAILSTREAM *stream;
  4489.     long    msgno;
  4490.     char       *cur, *body;
  4491. } raw_pipe;
  4492.  
  4493.  
  4494. int
  4495. raw_pipe_getc(c)
  4496.      unsigned char *c;
  4497. {
  4498.     if((!raw_pipe.cur
  4499.     && !(raw_pipe.cur = mail_fetchheader(raw_pipe.stream, raw_pipe.msgno)))
  4500.        || (!*raw_pipe.cur && !raw_pipe.body
  4501.        && !(raw_pipe.cur = raw_pipe.body = mail_fetchtext(raw_pipe.stream,
  4502.                                   raw_pipe.msgno)))
  4503.        || (!*raw_pipe.cur && raw_pipe.body))
  4504.       return(0);
  4505.  
  4506.     *c = (unsigned char) *raw_pipe.cur++;
  4507.     return(1);
  4508. }
  4509.  
  4510.  
  4511. void
  4512. prime_raw_text_getc(stream, msgno)
  4513.     MAILSTREAM *stream;
  4514.     long    msgno;
  4515. {
  4516.     raw_pipe.stream = stream;
  4517.     raw_pipe.msgno  = msgno;
  4518.     raw_pipe.cur    = raw_pipe.body = NULL;
  4519. }
  4520.  
  4521.  
  4522.  
  4523. /*----------------------------------------------------------------------
  4524.     Pipe message text
  4525.  
  4526.    Args: state -- various pine state bits
  4527.      msgmap -- Message number mapping table
  4528.      agg -- whether or not to aggregate the command on selected msgs
  4529.  
  4530.    Filters the original header and sends stuff to specified command
  4531.   ---*/
  4532. void
  4533. cmd_pipe(state, msgmap, agg)
  4534.      struct pine *state;
  4535.      MSGNO_S *msgmap;
  4536.      int      agg;
  4537. {
  4538.     ENVELOPE      *e;
  4539.     BODY      *b;
  4540.     PIPE_S      *syspipe;
  4541.     char          *resultfilename = NULL, prompt[80];
  4542.     int            done = 0, flags;
  4543.     gf_io_t       pc;
  4544.     int           next = 0;
  4545.     long           i;
  4546.     HelpType       help;
  4547.     static       capture = 1, raw = 0, delimit = 0, newpipe = 0;
  4548.     static char    pipe_command[MAXPATH+1];
  4549.     static ESCKEY_S pipe_opt[] = {
  4550.     {0, 0, "", ""},
  4551.     {ctrl('W'), 10, "^W", NULL},
  4552.     {ctrl('Y'), 11, "^Y", NULL},
  4553.     {ctrl('R'), 12, "^R", NULL},
  4554.     {0, 13, "^T", NULL},
  4555.     {-1, 0, NULL, NULL}
  4556.     };
  4557.  
  4558.     if(ps_global->restricted){
  4559.     q_status_message(SM_ORDER | SM_DING, 0, 4,
  4560.              "Pine demo can't pipe messages");
  4561.     return;
  4562.     }
  4563.     else if(!any_messages(msgmap, NULL, "to Pipe"))
  4564.       return;
  4565.  
  4566.     if(agg){
  4567.     if(!pseudo_selected(msgmap))
  4568.       return;
  4569.     else
  4570.       pipe_opt[4].ch = ctrl('T');
  4571.     }
  4572.     else
  4573.       pipe_opt[4].ch = -1;
  4574.  
  4575.     help = NO_HELP;
  4576.     while (!done) {
  4577.     sprintf(prompt, "Pipe %smessage%s%s to %s%s%s%s%s%s%s: ",
  4578.         raw ? "RAW " : "",
  4579.         agg ? "s" : " ",
  4580.         agg ? "" : comatose(mn_get_cur(msgmap)),
  4581.         (!capture || delimit || (newpipe && agg)) ? "(" : "",
  4582.         capture ? "" : "uncaptured",
  4583.         (!capture && delimit) ? "," : "",
  4584.         delimit ? "delimited" : "",
  4585.         ((!capture || delimit) && newpipe && agg) ? "," : "",
  4586.         (newpipe && agg) ? "new pipe" : "",
  4587.         (!capture || delimit || (newpipe && agg)) ? ") " : "");
  4588.     pipe_opt[1].label = raw ? "Shown Text" : "Raw Text";
  4589.     pipe_opt[2].label = capture ? "Free Output" : "Capture Output";
  4590.     pipe_opt[3].label = delimit ? "No Delimiter" : "With Delimiter";
  4591.     pipe_opt[4].label = newpipe ? "To Same Pipe" : "To Individual Pipes";
  4592.     switch(optionally_enter(pipe_command, -FOOTER_ROWS(state), 0,
  4593.                 MAXPATH, 1, 0, prompt, pipe_opt, help, 0)){
  4594.       case -1 :
  4595.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  4596.                  "Internal problem encountered");
  4597.         done++;
  4598.         break;
  4599.       
  4600.       case 10 :            /* flip raw bit */
  4601.         raw = !raw;
  4602.         break;
  4603.  
  4604.       case 11 :            /* flip capture bit */
  4605.         capture = !capture;
  4606.         break;
  4607.  
  4608.       case 12 :            /* flip delimit bit */
  4609.         delimit = !delimit;
  4610.         break;
  4611.  
  4612.       case 13 :            /* flip newpipe bit */
  4613.         newpipe = !newpipe;
  4614.         break;
  4615.  
  4616.       case 0 :
  4617.         if(pipe_command[0]){
  4618.         flags = PIPE_USER | PIPE_WRITE | PIPE_STDERR;
  4619.         if(!capture){
  4620. #ifndef    _WINDOWS
  4621.             ClearScreen();
  4622.             fflush(stdout);
  4623.             clear_cursor_pos();
  4624.             ps_global->mangled_screen = 1;
  4625. #endif
  4626.             flags |= PIPE_RESET;
  4627.         }
  4628.  
  4629.         if(!newpipe && !(syspipe = cmd_pipe_open(pipe_command,
  4630.                              (flags & PIPE_RESET)
  4631.                                ? NULL
  4632.                                : &resultfilename,
  4633.                              flags, &pc)))
  4634.           done++;
  4635.  
  4636.         for(i = mn_first_cur(msgmap);
  4637.             i > 0L && !done;
  4638.             i = mn_next_cur(msgmap)){
  4639.             e = mail_fetchstructure(ps_global->mail_stream,
  4640.                         mn_m2raw(msgmap, i), &b);
  4641.  
  4642.             if((newpipe
  4643.             && !(syspipe = cmd_pipe_open(pipe_command,
  4644.                              (flags & PIPE_RESET)
  4645.                                ? NULL
  4646.                                : &resultfilename,
  4647.                              flags, &pc)))
  4648.                || (delimit && !bezerk_delimiter(e, pc, next++)))
  4649.               done++;
  4650.  
  4651.             if(!done){
  4652.             if(raw){
  4653.                 char    *pipe_err;
  4654.  
  4655.                 prime_raw_text_getc(ps_global->mail_stream,
  4656.                         mn_m2raw(msgmap, i));
  4657.                 gf_filter_init();
  4658.                 gf_link_filter(gf_nvtnl_local);
  4659.                 if(pipe_err = gf_pipe(raw_pipe_getc, pc)){
  4660.                 q_status_message1(SM_ORDER|SM_DING,
  4661.                           3, 3,
  4662.                           "Internal Error: %s",
  4663.                           pipe_err);
  4664.                 done++;
  4665.                 }
  4666.             }
  4667.             else if(!format_message(mn_m2raw(msgmap, i), e, b,
  4668.                         (FM_NEW_MESS), pc))
  4669.               done++;
  4670.             }
  4671.  
  4672.             if(newpipe)
  4673.               (void) close_system_pipe(&syspipe);
  4674.         }
  4675.  
  4676.         if(!newpipe)
  4677.           (void) close_system_pipe(&syspipe);
  4678.  
  4679.         if(done)        /* say we had a problem */
  4680.           q_status_message(SM_ORDER | SM_DING, 3, 3,
  4681.                    "Error piping message");
  4682.         else if(resultfilename){
  4683.             /* only display if no error */
  4684.             display_output_file(resultfilename, "PIPE MESSAGE", NULL);
  4685.             fs_give((void **)&resultfilename);
  4686.         }
  4687.         else
  4688.           q_status_message(SM_ORDER, 0, 2, "Pipe command completed");
  4689.  
  4690.         done++;
  4691.         break;
  4692.         }
  4693.         /* else fall thru as if cancelled */
  4694.  
  4695.       case 1 :
  4696.         cmd_cancelled("Pipe command");
  4697.         done++;
  4698.         break;
  4699.  
  4700.       case 3 :
  4701.             help = (help == NO_HELP) ? h_pipe_msg : NO_HELP;
  4702.         break;
  4703.  
  4704.       case 2 :                              /* no place to escape to */
  4705.       case 4 :                              /* can't suspend */
  4706.       default :
  4707.         break;   
  4708.     }
  4709.     }
  4710.  
  4711.     ps_global->mangled_footer = 1;
  4712.     if(agg)
  4713.       restore_selected(msgmap);
  4714. }
  4715.  
  4716.  
  4717.  
  4718. /*----------------------------------------------------------------------
  4719.   Actually open the pipe used to write piped data down
  4720.  
  4721.    Args: 
  4722.    Returns: TRUE if success, otherwise FALSE
  4723.  
  4724.   ----*/
  4725. PIPE_S *
  4726. cmd_pipe_open(cmd, result, flags, pc)
  4727.     char     *cmd;
  4728.     char    **result;
  4729.     int       flags;
  4730.     gf_io_t  *pc;
  4731. {
  4732.     PIPE_S *pipe;
  4733.  
  4734.     if(pipe = open_system_pipe(cmd, result, NULL, flags))
  4735.       gf_set_writec(pc, pipe->out.f, 0L, FileStar);
  4736.     else
  4737.       q_status_message(SM_ORDER | SM_DING, 3, 3, "Error opening pipe") ;
  4738.  
  4739.     return(pipe);
  4740. }
  4741.  
  4742.  
  4743.  
  4744. /*----------------------------------------------------------------------
  4745.  Prompt the user for the type of sort desired
  4746.  
  4747.    Args: none
  4748.    Returns: 0 if search OK (matching numbers selected by side effect)
  4749.             1 if there's a problem
  4750.  
  4751.    NOTE: any and all functions that successfully exit the second
  4752.      switch() statement below (currently "select_*() functions"),
  4753.      *MUST* update the folder's MESSAGECACHE element's "searched"
  4754.      bits to reflect the search result.  Functions using
  4755.      mail_search() get this for free, the others must update 'em
  4756.      by hand.
  4757.  
  4758.   ----*/
  4759. void
  4760. aggregate_select(state, msgmap, q_line, in_index)
  4761.     struct pine *state;
  4762.     MSGNO_S     *msgmap;
  4763.     int      q_line, in_index;
  4764. {
  4765.     long       i, diff, old_tot, msgno;
  4766.     int        q = 0, rv = 0, narrow = 0, hidden;
  4767.     HelpType   help = NO_HELP;
  4768.     ESCKEY_S  *sel_opts;
  4769.     extern     MAILSTREAM *mm_search_stream;
  4770.     extern     long       mm_search_count;
  4771.  
  4772.     hidden           = any_lflagged(msgmap, MN_HIDE) > 0L;
  4773.     mm_search_stream = state->mail_stream;
  4774.     mm_search_count  = 0L;
  4775.  
  4776.     sel_opts = sel_opts2;
  4777.     if(old_tot = any_lflagged(msgmap, MN_SLCT)){
  4778.     i = get_lflag(state->mail_stream, msgmap, mn_get_cur(msgmap), MN_SLCT);
  4779.     sel_opts1[1].label = "unselect Cur" + (i ? 0 : 2);
  4780.     sel_opts += 2;            /* disable extra options */
  4781.     switch(q = radio_buttons(sel_pmt1, q_line, sel_opts1, 'c', 'x', help,
  4782.                  RB_NORM)){
  4783.       case 'f' :            /* flip selection */
  4784.         msgno = 0L;
  4785.         for(i = 1L; i <= mn_get_total(msgmap); i++){
  4786.         q = !get_lflag(state->mail_stream, msgmap, i, MN_SLCT);
  4787.         set_lflag(state->mail_stream, msgmap, i, MN_SLCT, q);
  4788.         if(hidden){
  4789.             set_lflag(state->mail_stream, msgmap, i, MN_HIDE, !q);
  4790.             if(!msgno && q)
  4791.               mn_reset_cur(msgmap, msgno = i);
  4792.         }
  4793.         }
  4794.  
  4795.         return;
  4796.  
  4797.       case 'n' :            /* narrow selection */
  4798.         narrow++;
  4799.       case 'b' :            /* broaden selection */
  4800.         q = 0;            /* but don't offer criteria prompt */
  4801.         break;
  4802.  
  4803.       case 'c' :            /* Un/Select Current */
  4804.       case 'a' :            /* Unselect All */
  4805.       case 'x' :            /* cancel */
  4806.         break;
  4807.  
  4808.       default :
  4809.         q_status_message(SM_ORDER | SM_DING, 3, 3,
  4810.                  "Unsupported Select option");
  4811.         return;
  4812.     }
  4813.     }
  4814.  
  4815.     if(!q)
  4816.       q = radio_buttons(sel_pmt2, q_line, sel_opts, 'c', 'x', help, RB_NORM);
  4817.  
  4818.     /*
  4819.      * NOTE: See note about MESSAGECACHE "searched" bits above!
  4820.      */
  4821.     switch(q){
  4822.       case 'x':                /* cancel */
  4823.     cmd_cancelled("Select command");
  4824.     return;
  4825.  
  4826.       case 'c' :            /* select/unselect current */
  4827.     (void) individual_select(state, msgmap, q_line, in_index);
  4828.     return;
  4829.  
  4830.       case 'a' :            /* select/unselect all */
  4831.     msgno = any_lflagged(msgmap, MN_SLCT);
  4832.     diff    = (!msgno) ? mn_get_total(msgmap) : 0L;
  4833.  
  4834.     for(i = 1L; i <= mn_get_total(msgmap); i++){
  4835.         if(msgno){        /* unmark 'em all */
  4836.         if(get_lflag(state->mail_stream, msgmap, i, MN_SLCT)){
  4837.             diff++;
  4838.             set_lflag(state->mail_stream, msgmap, i, MN_SLCT, 0);
  4839.         }
  4840.         else if(hidden)
  4841.           set_lflag(state->mail_stream, msgmap, i, MN_HIDE, 0);
  4842.         }
  4843.         else            /* mark 'em all */
  4844.           set_lflag(state->mail_stream, msgmap, i, MN_SLCT, 1);
  4845.     }
  4846.  
  4847.     q_status_message4(SM_ORDER,0,2,"%s%s message%s %sselected",
  4848.               msgno ? "" : "All ", comatose(diff), 
  4849.               plural(diff), msgno ? "UN" : "");
  4850.     return;
  4851.  
  4852.       case 'n' :            /* Select by Number */
  4853.     rv = select_number(state->mail_stream, msgmap, mn_get_cur(msgmap));
  4854.     break;
  4855.  
  4856.       case 'd' :            /* Select by Date */
  4857.     rv = select_date(state->mail_stream, msgmap, mn_get_cur(msgmap));
  4858.     break;
  4859.  
  4860.       case 't' :            /* Text */
  4861.     rv = select_text(state->mail_stream, msgmap, mn_get_cur(msgmap));
  4862.     break;
  4863.  
  4864.       case 's' :            /* Status */
  4865.     rv = select_flagged(state->mail_stream, msgmap, mn_get_cur(msgmap));
  4866.     break;
  4867.  
  4868.       default :
  4869.     q_status_message(SM_ORDER | SM_DING, 3, 3,
  4870.              "Unsupported Select option");
  4871.     return;
  4872.     }
  4873.  
  4874.     if(rv)                /* bad return value.. */
  4875.       return;                /* error already displayed */
  4876.  
  4877.     if(narrow)                /* make sure something was selected */
  4878.       for(i = 1L; i <= mn_get_total(msgmap); i++)
  4879.     if(mail_elt(state->mail_stream, mn_m2raw(msgmap, i))->searched){
  4880.         if(get_lflag(state->mail_stream, msgmap, i, MN_SLCT))
  4881.           break;
  4882.         else
  4883.           mm_search_count--;
  4884.     }
  4885.  
  4886.     diff = 0L;
  4887.     if(mm_search_count){
  4888.     /*
  4889.      * loop thru all the messages, adjusting local flag bits
  4890.      * based on their "searched" bit...
  4891.      */
  4892.     for(i = 1L, msgno = 0L; i <= mn_get_total(msgmap); i++)
  4893.       if(narrow){
  4894.           /* turning OFF selectedness if the "searched" bit isn't lit. */
  4895.           if(get_lflag(state->mail_stream, msgmap, i, MN_SLCT)){
  4896.           if(!mail_elt(state->mail_stream,
  4897.                    mn_m2raw(msgmap, i))->searched){
  4898.               diff--;
  4899.               set_lflag(state->mail_stream, msgmap, i, MN_SLCT, 0);
  4900.               if(hidden)
  4901.             set_lflag(state->mail_stream, msgmap, i, MN_HIDE, 1);
  4902.           }
  4903.           else if(msgno < mn_get_cur(msgmap))
  4904.             msgno = i;
  4905.           }
  4906.       }
  4907.       else if(mail_elt(state->mail_stream,mn_m2raw(msgmap,i))->searched){
  4908.           /* turn ON selectedness if "searched" bit is lit. */
  4909.           if(!get_lflag(state->mail_stream, msgmap, i, MN_SLCT)){
  4910.           diff++;
  4911.           set_lflag(state->mail_stream, msgmap, i, MN_SLCT, 1);
  4912.           if(hidden)
  4913.             set_lflag(state->mail_stream, msgmap, i, MN_HIDE, 0);
  4914.           }
  4915.       }
  4916.  
  4917.     /* if we're zoomed and the current message was unselected */
  4918.     if(narrow && msgno
  4919.        && get_lflag(state->mail_stream,msgmap,mn_get_cur(msgmap),MN_HIDE))
  4920.       mn_reset_cur(msgmap, msgno);
  4921.     }
  4922.  
  4923.     if(!diff){
  4924.     if(narrow)
  4925.       q_status_message4(SM_ORDER, 0, 2,
  4926.             "%s.  %s message%s remain%s selected.",
  4927.             mm_search_count ? "No change resulted"
  4928.                     : "No messages in intersection",
  4929.             comatose(old_tot), plural(old_tot),
  4930.             (old_tot == 1L) ? "s" : "");
  4931.     else if(old_tot && mm_search_count)
  4932.       q_status_message(SM_ORDER, 0, 2,
  4933.            "No change resulted.  Matching messages already selected.");
  4934.     else
  4935.       q_status_message1(SM_ORDER | SM_DING, 0, 2,
  4936.                 "Select failed!  No %smessages selected.",
  4937.                 old_tot ? "additional " : "");
  4938.     }
  4939.     else if(old_tot){
  4940.     sprintf(tmp_20k_buf,
  4941.         "Select matched %ld message%s!  %s %smessage%s %sselected.",
  4942.         (diff > 0) ? diff : old_tot + diff,
  4943.         plural((diff > 0) ? diff : old_tot + diff),
  4944.         comatose((diff > 0) ? any_lflagged(msgmap, MN_SLCT) : -diff),
  4945.         (diff > 0) ? "total " : "",
  4946.         plural((diff > 0) ? any_lflagged(msgmap, MN_SLCT) : -diff),
  4947.         (diff > 0) ? "" : "UN");
  4948.     q_status_message(SM_ORDER, 0, 2, tmp_20k_buf);
  4949.     }
  4950.     else
  4951.       q_status_message2(SM_ORDER, 0, 2, "Select matched %s message%s!",
  4952.             comatose(diff), plural(diff));
  4953. }
  4954.  
  4955.  
  4956.  
  4957. /*----------------------------------------------------------------------
  4958.  Toggle the state of the current message
  4959.  
  4960.    Args: state -- pointer pine's state variables
  4961.      msgmap -- message collection to operate on
  4962.      q_line -- line on display to write prompts
  4963.      in_index -- in the message index view
  4964.    Returns: TRUE if current marked selected, FALSE otw
  4965.   ----*/
  4966. int
  4967. individual_select(state, msgmap, q_line, in_index)
  4968.      struct pine *state;
  4969.      MSGNO_S     *msgmap;
  4970.      int      q_line, in_index;
  4971. {
  4972.     long i;
  4973.     int  rv;
  4974.  
  4975.     i = mn_get_cur(msgmap);
  4976.     if(rv = get_lflag(state->mail_stream, msgmap, i, MN_SLCT)){ /* set? */
  4977.     set_lflag(state->mail_stream, msgmap, i, MN_SLCT, 0);
  4978.     if(any_lflagged(msgmap, MN_HIDE) > 0L){
  4979.         set_lflag(state->mail_stream, msgmap, i, MN_HIDE, 1);
  4980.         /*
  4981.          * See if there's anything left to zoom on.  If so, 
  4982.          * pick an adjacent one for highlighting, else make
  4983.          * sure nothing is left hidden...
  4984.          */
  4985.         if(any_lflagged(msgmap, MN_SLCT)){
  4986.         mn_inc_cur(state->mail_stream, msgmap);
  4987.         if(mn_get_cur(msgmap) == i)
  4988.           mn_dec_cur(state->mail_stream, msgmap);
  4989.         }
  4990.         else{            /* clear all hidden flags */
  4991.         for(i = 1L; i <= mn_get_total(msgmap); i++)
  4992.           set_lflag(state->mail_stream, msgmap, i, MN_HIDE, 0);
  4993.         }
  4994.     }
  4995.     }
  4996.     else
  4997.       set_lflag(state->mail_stream, msgmap, i, MN_SLCT, 1);
  4998.  
  4999.     if(!in_index)
  5000.       q_status_message2(SM_ORDER, 0, 2, "Message %s %sselected",
  5001.             long2string(i), rv ? "UN" : "");
  5002.  
  5003.     return(!rv);
  5004. }
  5005.  
  5006.  
  5007.  
  5008. /*----------------------------------------------------------------------
  5009.  Prompt the user for the command to perform on selected messages
  5010.  
  5011.    Args: state -- pointer pine's state variables
  5012.      msgmap -- message collection to operate on
  5013.      q_line -- line on display to write prompts
  5014.    Returns: 1 if the selected messages are suitably commanded,
  5015.         0 if the choice to pick the command was declined
  5016.  
  5017.   ----*/
  5018. int
  5019. apply_command(state, msgmap, q_line)
  5020.      struct pine *state;
  5021.      MSGNO_S     *msgmap;
  5022.      int      q_line;
  5023. {
  5024.     int i = 8, rv = 1;
  5025.     int we_cancel = 0;
  5026.  
  5027.     if(F_ON(F_ENABLE_FLAG,state)){ /* flag? */
  5028.     sel_opts3[i].ch      = '*';
  5029.     sel_opts3[i].rval    = '*';
  5030.     sel_opts3[i].name    = "*";
  5031.     sel_opts3[i++].label = "Flag";
  5032.     }
  5033.  
  5034.     if(F_ON(F_ENABLE_PIPE,state)){ /* pipe? */
  5035.     sel_opts3[i].ch      = '|';
  5036.     sel_opts3[i].rval    = '|';
  5037.     sel_opts3[i].name    = "|";
  5038.     sel_opts3[i++].label = "Pipe";
  5039.     }
  5040.  
  5041.     /*
  5042.      * This doesn't fit on the normal keymenu, so it will go in the help
  5043.      * slot instead (see "hacking" in status.c).  If either of above two
  5044.      * commands are disabled then it does fit.
  5045.      */
  5046.     if(F_ON(F_ENABLE_BOUNCE,state)){ /* bounce? */
  5047.     sel_opts3[i].ch      = 'b';
  5048.     sel_opts3[i].rval    = 'b';
  5049.     sel_opts3[i].name    = "B";
  5050.     sel_opts3[i++].label = "Bounce";
  5051.     }
  5052.  
  5053.     sel_opts3[i].ch = -1;
  5054.     /*
  5055.      * Can't put an actual help in here instead of NO_HELP.  See comment
  5056.      * above.
  5057.      */
  5058.     switch(radio_buttons(sel_pmt3,q_line,sel_opts3,0,'x',NO_HELP,RB_NORM)){
  5059.       case 'd' :            /* delete */
  5060.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  5061.     cmd_delete(state, msgmap, 1);
  5062.     if(we_cancel)
  5063.       cancel_busy_alarm(0);
  5064.     break;
  5065.  
  5066.       case 'u' :            /* undelete */
  5067.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  5068.     cmd_undelete(state, msgmap, 1);
  5069.     if(we_cancel)
  5070.       cancel_busy_alarm(0);
  5071.     break;
  5072.  
  5073.       case 'r' :            /* reply */
  5074.     cmd_reply(state, msgmap, 1);
  5075.     break;
  5076.  
  5077.       case 'f' :            /* Forward */
  5078.     cmd_forward(state, msgmap, 1);
  5079.     break;
  5080.  
  5081.       case 'y' :            /* prYnt */
  5082.     cmd_print(state, msgmap, 1, 1);
  5083.     break;
  5084.  
  5085.       case 't' :            /* take address */
  5086.     cmd_take_addr(state, msgmap, 1);
  5087.     break;
  5088.  
  5089.       case 's' :            /* save */
  5090.     cmd_save(state, msgmap, q_line, 1);
  5091.     break;
  5092.  
  5093.       case 'e' :            /* export */
  5094.     cmd_export(state, msgmap, q_line, 1);
  5095.     break;
  5096.  
  5097.       case '|' :            /* pipe */
  5098.     cmd_pipe(state, msgmap, 1);
  5099.     break;
  5100.  
  5101.       case '*' :            /* flag */
  5102.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  5103.     cmd_flag(state, msgmap, 1);
  5104.     if(we_cancel)
  5105.       cancel_busy_alarm(0);
  5106.     break;
  5107.  
  5108.       case 'b' :            /* bounce */
  5109.     cmd_bounce(state, msgmap, 1);
  5110.     break;
  5111.  
  5112.       case 'x' :            /* cancel */
  5113.     cmd_cancelled("Apply command");
  5114.     rv = 0;
  5115.     break;
  5116.  
  5117.     default:
  5118.     break;
  5119.     }
  5120.  
  5121.     return(rv);
  5122. }
  5123.  
  5124.  
  5125.  
  5126. /*----------------------------------------------------------------------
  5127.   ZOOM the message index (set any and all necessary hidden flag bits)
  5128.  
  5129.    Args: state -- usual pine state
  5130.      msgmap -- usual message mapping
  5131.    Returns: number of messages zoomed in on
  5132.  
  5133.   ----*/
  5134. long
  5135. zoom_index(state, msgmap, cur_msgno)
  5136.     struct pine *state;
  5137.     MSGNO_S    *msgmap;
  5138.     long    *cur_msgno;
  5139. {
  5140.     long i, count = 0L, first = 0L;
  5141.  
  5142.     if(any_lflagged(msgmap, MN_SLCT)){
  5143.     for(i = 1L; i <= mn_get_total(msgmap); i++){
  5144.         if(!get_lflag(state->mail_stream, msgmap, i, MN_SLCT)){
  5145.         set_lflag(state->mail_stream, msgmap, i, MN_HIDE, 1);
  5146.         }
  5147.         else{
  5148.         count++;
  5149.         if(!first)
  5150.           first = i;
  5151.         }
  5152.     }
  5153.  
  5154.     if(!get_lflag(state->mail_stream, msgmap, *cur_msgno, MN_SLCT))
  5155.       mn_set_cur(msgmap, *cur_msgno = first);
  5156.     }
  5157.  
  5158.     return(count);
  5159. }
  5160.  
  5161.  
  5162.  
  5163. /*----------------------------------------------------------------------
  5164.   UnZOOM the message index (clear any and all hidden flag bits)
  5165.  
  5166.    Args: state -- usual pine state
  5167.      msgmap -- usual message mapping
  5168.    Returns: 1 if hidden bits to clear and they were, 0 if none to clear
  5169.  
  5170.   ----*/
  5171. int
  5172. unzoom_index(state, msgmap)
  5173.     struct pine *state;
  5174.     MSGNO_S    *msgmap;
  5175. {
  5176.     register long i;
  5177.  
  5178.     if(!any_lflagged(msgmap, MN_HIDE))
  5179.       return(0);
  5180.  
  5181.     for(i = 1L; i <= mn_get_total(msgmap); i++)
  5182.       if(get_lflag(state->mail_stream, msgmap, i, MN_HIDE))
  5183.     set_lflag(state->mail_stream, msgmap, i, MN_HIDE, 0);
  5184.  
  5185.     return(1);
  5186. }
  5187.  
  5188.  
  5189.  
  5190. /*----------------------------------------------------------------------
  5191.  Prompt the user for the type of sort he desires
  5192.  
  5193.    Args: none
  5194.    Returns: 0 if search OK (matching numbers selected by side effect)
  5195.             1 if there's a problem
  5196.  
  5197.   ----*/
  5198. int
  5199. select_number(stream, msgmap, msgno)
  5200.      MAILSTREAM *stream;
  5201.      MSGNO_S    *msgmap;
  5202.      long     msgno;
  5203. {
  5204.     int r;
  5205.     long n1, n2;
  5206.     char number1[16], number2[16], numbers[80], *p, *t;
  5207.     HelpType help;
  5208.  
  5209.     numbers[0] = '\0';
  5210.     ps_global->mangled_footer = 1;
  5211.     help = NO_HELP;
  5212.     while(1){
  5213.         r = optionally_enter(numbers, -FOOTER_ROWS(ps_global), 0, 79, 1, 0,
  5214.                              select_num, NULL, help, 0);
  5215.         if(r == 4)
  5216.       continue;
  5217.  
  5218.         if(r == 3){
  5219.             help = (help == NO_HELP) ? h_select_by_num : NO_HELP;
  5220.         continue;
  5221.     }
  5222.  
  5223.     for(t = p = numbers; *p ; p++)    /* strip whitespace */
  5224.       if(!isspace((unsigned char)*p))
  5225.         *t++ = *p;
  5226.  
  5227.     *t = '\0';
  5228.  
  5229.         if(r == 1 || numbers[0] == '\0'){
  5230.         cmd_cancelled("Selection by number");
  5231.         return(1);
  5232.         }
  5233.     else
  5234.       break;
  5235.     }
  5236.  
  5237.     for(n1 = 1; n1 <= stream->nmsgs; n1++)
  5238.       mail_elt(stream, n1)->searched = 0;    /* clear searched bits */
  5239.  
  5240.     for(p = numbers; *p ; p++){
  5241.     t = number1;
  5242.     while(*p && isdigit((unsigned char)*p))
  5243.       *t++ = *p++;
  5244.  
  5245.     *t = '\0';
  5246.     if((n1 = atol(number1)) < 1L || n1 > mn_get_total(msgmap)){
  5247.         q_status_message1(SM_ORDER | SM_DING, 0, 2,
  5248.                   "%s out of message number range",
  5249.                   long2string(n1));
  5250.         return(1);
  5251.     }
  5252.  
  5253.     t = number2;
  5254.     if(*p && *p == '-'){
  5255.         while(*++p && isdigit((unsigned char)*p))
  5256.           *t++ = *p;
  5257.  
  5258.         *t = '\0';
  5259.         if((n2 = atol(number2)) < 1L 
  5260.            || n2 > mn_get_total(msgmap)){
  5261.         q_status_message1(SM_ORDER | SM_DING, 0, 2,
  5262.                   "%s out of message number range",
  5263.                   long2string(n2));
  5264.         return(1);
  5265.         }
  5266.  
  5267.         if(n2 <= n1){
  5268.         q_status_message(SM_ORDER | SM_DING, 0, 2,
  5269.                  "Invalid message number range");
  5270.         break;
  5271.         }
  5272.  
  5273.         for(;n1 <= n2; n1++)
  5274.           mail_searched(stream, mn_m2raw(msgmap, n1));
  5275.     }
  5276.     else
  5277.       mail_searched(stream, mn_m2raw(msgmap, n1));
  5278.  
  5279.     if(*p == '\0')
  5280.       break;
  5281.     }
  5282.     
  5283.     return(0);
  5284. }
  5285.  
  5286.  
  5287.  
  5288. /*----------------------------------------------------------------------
  5289.  Prompt the user for the type of sort he desires
  5290.  
  5291.    Args: none
  5292.    Returns: 0 if search OK (matching numbers selected by side effect)
  5293.             1 if there's a problem
  5294.  
  5295.   ----*/
  5296. int
  5297. select_date(stream, msgmap, msgno)
  5298.      MAILSTREAM *stream;
  5299.      MSGNO_S    *msgmap;
  5300.      long     msgno;
  5301. {
  5302.     int  r, we_cancel = 0, when = 0;
  5303.     char date[32], defdate[32], prompt[128];
  5304.     time_t       seldate = time(0);
  5305.     struct tm *seldate_tm;
  5306.     static char *when_range[] = {"ON", "SINCE", "BEFORE"},
  5307.         *when_mod[] = {"", " (inclusive)", " (exclusive)"};
  5308.  
  5309.     date[0] = '\0';
  5310.     ps_global->mangled_footer = 1;
  5311.  
  5312.     while(1){
  5313.     seldate_tm = localtime(&seldate);
  5314.     sprintf(defdate, "%.2d-%.4s-%.4d", seldate_tm->tm_mday,
  5315.         month_abbrev(seldate_tm->tm_mon + 1),
  5316.         seldate_tm->tm_year + 1900);
  5317.     sprintf(prompt,"Select messages arriving %s%s [%s]: ",
  5318.         when_range[when], when_mod[when], defdate);
  5319.     r = optionally_enter(date,-FOOTER_ROWS(ps_global), 0, 31, 1, 0,
  5320.                  prompt, sel_date_opt, NO_HELP, 0);
  5321.     switch (r){
  5322.       case 1 :
  5323.         cmd_cancelled("Selection by date");
  5324.         return(1);
  5325.  
  5326.       case 3 :
  5327.       case 4 :
  5328.         continue;
  5329.  
  5330.       case 11 :
  5331.         {
  5332.         MESSAGECACHE *mc;
  5333.  
  5334.         if(mc = mail_elt(stream, mn_m2raw(msgmap, msgno))){
  5335.             /* mail_date returns fixed field width date */
  5336.             *(mail_date(tmp_20k_buf, mc) + 11) = '\0';
  5337.             strcpy(date, tmp_20k_buf);
  5338.         }
  5339.         }
  5340.  
  5341.         continue;
  5342.  
  5343.       case 12 :            /* set default to PREVIOUS day */
  5344.         seldate -= 86400;
  5345.         continue;
  5346.  
  5347.       case 13 :            /* set default to NEXT day */
  5348.         seldate += 86400;
  5349.         continue;
  5350.  
  5351.       case 14 :
  5352.         when = ++when % (sizeof(when_range) / sizeof(char *));
  5353.         continue;
  5354.  
  5355.       default:
  5356.         break;
  5357.     }
  5358.  
  5359.     removing_leading_white_space(date);
  5360.     removing_trailing_white_space(date);
  5361.     if(!*date)
  5362.       strcpy(date, defdate);
  5363.  
  5364.     break;
  5365.     }
  5366.  
  5367.     we_cancel = busy_alarm(1, "Busy Selecting", NULL, 0);
  5368.     sprintf(prompt, "%s %s", when_range[when], date);
  5369.     mail_search(stream, prompt);
  5370.     if(we_cancel)
  5371.       cancel_busy_alarm(0);
  5372.  
  5373.     return(0);
  5374. }
  5375.  
  5376.  
  5377.  
  5378. /*----------------------------------------------------------------------
  5379.  Prompt the user for the type of sort he desires
  5380.  
  5381.    Args: none
  5382.    Returns: 0 if search OK (matching numbers selected by side effect)
  5383.             1 if there's a problem
  5384.  
  5385.   ----*/
  5386. int
  5387. select_text(stream, msgmap, msgno)
  5388.      MAILSTREAM *stream;
  5389.      MSGNO_S    *msgmap;
  5390.      long     msgno;
  5391. {
  5392.     int       r, type, we_cancel = 0;
  5393.     char     *sval = NULL, sstring[80], tmp[128];
  5394.     ESCKEY_S  ekey[3];
  5395.     ENVELOPE *env = NULL;
  5396.     HelpType  help;
  5397.  
  5398.     ps_global->mangled_footer = 1;
  5399.  
  5400.     /*
  5401.      * prepare some friendly defaults...
  5402.      */
  5403.     ekey[1].ch   = -1;
  5404.     ekey[2].ch   = -1;
  5405.     switch(type = radio_buttons(sel_text, -FOOTER_ROWS(ps_global),
  5406.                 sel_text_opt, 's', 'x', NO_HELP, RB_NORM)){
  5407.       case 't' :            /* address fields, offer To or From */
  5408.       case 'f' :
  5409.       case 'c' :
  5410.     sval          = (type == 't') ? "TO" : (type == 'f') ? "FROM" : "CC";
  5411.     ekey[0].ch    = ctrl('T');
  5412.     ekey[0].name  = "^T";
  5413.     ekey[0].rval  = 10;
  5414.     ekey[0].label = "Cur To";
  5415.     ekey[1].ch    = ctrl('R');
  5416.     ekey[1].name  = "^R";
  5417.     ekey[1].rval  = 11;
  5418.     ekey[1].label = "Cur From";
  5419.     break;
  5420.  
  5421.       case 's' :
  5422.     sval          = "SUBJECT";
  5423.     ekey[0].ch    = ctrl('X');
  5424.     ekey[0].name  = "^X";
  5425.     ekey[0].rval  = 13;
  5426.     ekey[0].label = "Cur Subject";
  5427.     break;
  5428.  
  5429.       case 'a' :
  5430.     sval = "TEXT";            /* fall thru */
  5431.     ekey[0].ch = -1;
  5432.     break;
  5433.  
  5434.       case 'x':
  5435.     break;
  5436.  
  5437.       default:
  5438.     dprint(1, (debugfile,"\n - BOTCH: select_text unrecognized option\n"));
  5439.     return(1);
  5440.     }
  5441.  
  5442.     if(type != 'x'){
  5443.     if(ekey[0].ch > -1 && msgno > 0L
  5444.        && !(env=mail_fetchstructure(stream,mn_m2raw(msgmap,msgno),NULL)))
  5445.       ekey[0].ch = -1;
  5446.  
  5447.     sstring[0] = '\0';
  5448.     help = NO_HELP;
  5449.     r = type;
  5450.     while(r != 'x'){
  5451.         sprintf(tmp, "String in message %s to match : ", sval);
  5452.         switch(r = optionally_enter(sstring, -FOOTER_ROWS(ps_global), 0,
  5453.                     79, 1, 0, tmp, ekey, help, 0)){
  5454.           case 3 :
  5455.         help = (help == NO_HELP)
  5456.                 ? ((type == 'f') ? h_select_txt_from
  5457.                   : (type == 't') ? h_select_txt_to
  5458.                    : (type == 'c') ? h_select_txt_cc
  5459.                 : (type == 's') ? h_select_txt_subj
  5460.                  : (type == 'a') ? h_select_txt_all
  5461.                   :              NO_HELP)
  5462.                 : NO_HELP;
  5463.           case 4 :
  5464.         continue;
  5465.  
  5466.           case 10 :            /* To: default */
  5467.         if(env && env->to && env->to->mailbox)
  5468.           sprintf(sstring, "%.30s%s%.40s", env->to->mailbox,
  5469.               env->to->host ? "@" : "",
  5470.               env->to->host ? env->to->host : "");
  5471.         continue;
  5472.  
  5473.           case 11 :            /* From: default */
  5474.         if(env && env->from && env->from->mailbox)
  5475.           sprintf(sstring, "%.30s%s%.40s", env->from->mailbox,
  5476.               env->from->host ? "@" : "",
  5477.               env->from->host ? env->from->host : "");
  5478.         continue;
  5479.  
  5480.           case 12 :            /* Cc: default */
  5481.         if(env && env->cc && env->cc->mailbox)
  5482.           sprintf(sstring, "%.30s%s%.40s", env->cc->mailbox,
  5483.               env->cc->host ? "@" : "",
  5484.               env->cc->host ? env->cc->host : "");
  5485.         continue;
  5486.  
  5487.           case 13 :            /* Subject: default */
  5488.         if(env && env->subject && env->subject[0]){
  5489.             char *p = NULL;
  5490.             sprintf(sstring, "%.70s",
  5491.                 rfc1522_decode((unsigned char *)tmp_20k_buf,
  5492.                        env->subject, &p));
  5493.             if(p)
  5494.               fs_give((void **) &p);
  5495.         }
  5496.  
  5497.         continue;
  5498.  
  5499.           default :
  5500.         break;
  5501.         }
  5502.  
  5503.         if(r == 1 || sstring[0] == '\0')
  5504.           r = 'x';
  5505.  
  5506.         break;
  5507.     }
  5508.     }
  5509.  
  5510.     if(type == 'x' || r == 'x'){
  5511.     cmd_cancelled("Selection by text");
  5512.     return(1);
  5513.     }
  5514.  
  5515.     sprintf(tmp, "%s ", sval);
  5516.     sval = tmp + strlen(tmp);        /* sval overloaded! */
  5517.     if(strpbrk(sstring, "\012\015\"%{\\() "))
  5518.       sprintf(sval, "{%d}\015\012%s", strlen(sstring), sstring);
  5519.     else
  5520.       strcpy(sval, sstring);
  5521.  
  5522.     we_cancel = busy_alarm(1, "Busy Selecting", NULL, 0);
  5523.     mail_search(stream, tmp);
  5524.     if(we_cancel)
  5525.       cancel_busy_alarm(0);
  5526.  
  5527.     return(0);
  5528. }
  5529.  
  5530.  
  5531.  
  5532. /*----------------------------------------------------------------------
  5533.  Prompt the user for the type of sort he desires
  5534.  
  5535.    Args: none
  5536.    Returns: 0 if search OK (matching numbers selected by side effect)
  5537.             1 if there's a problem
  5538.  
  5539.   ----*/
  5540. int
  5541. select_flagged(stream, msgmap, msgno)
  5542.      MAILSTREAM *stream;
  5543.      MSGNO_S    *msgmap;
  5544.      long     msgno;
  5545. {
  5546.     int   s, not = 0, we_cancel = 0;
  5547.     char *flagp;
  5548.  
  5549.     while(1){
  5550.     s = radio_buttons((not) ? sel_flag_not : sel_flag,
  5551.               -FOOTER_ROWS(ps_global), sel_flag_opt, '*', 'x',
  5552.               NO_HELP, RB_NORM);
  5553.               
  5554.     if(s == 'x'){
  5555.         cmd_cancelled("Selection by status");
  5556.         return(1);
  5557.     }
  5558.     else if(s == '!')
  5559.       not = !not;
  5560.     else
  5561.       break;
  5562.     }
  5563.  
  5564.     flagp = cpystr((s == 'n') ? (not) ? "SEEN"
  5565.                       : "UNSEEN UNDELETED UNANSWERED" :
  5566.               (s == 'd') ? (not) ? "UNDELETED"
  5567.                      : "DELETED" :
  5568.              (s == 'a') ? (not) ? "UNANSWERED"
  5569.                         : "ANSWERED UNDELETED" :
  5570.                 (not) ? "UNFLAGGED" : "FLAGGED");
  5571.     we_cancel = busy_alarm(1, "Busy Selecting", NULL, 0);
  5572.     mail_search(stream, flagp);
  5573.     if(we_cancel)
  5574.       cancel_busy_alarm(0);
  5575.  
  5576.     fs_give((void **)&flagp);
  5577.     return(0);
  5578. }
  5579.  
  5580.  
  5581.  
  5582. /*----------------------------------------------------------------------
  5583.    Prompt the user for the type of sort he desires
  5584.  
  5585. Args: state -- pine state pointer
  5586.       q1 -- Line to prompt on
  5587.  
  5588.       Returns 0 if it was cancelled, 1 otherwise.
  5589.   ----*/
  5590. int
  5591. select_sort(state, ql)
  5592.      struct pine *state;
  5593.      int ql;
  5594. {
  5595.     char      prompt[200], tmp[3], *p;
  5596.     int       s, i;
  5597.     int       deefault = 'a', retval = 1;
  5598.     HelpType  help;
  5599.     ESCKEY_S  sorts[10];
  5600.  
  5601. #ifdef _WINDOWS
  5602.     DLG_SORTPARAM    sortsel;
  5603.  
  5604.     if (mswin_usedialog ()) {
  5605.  
  5606.     sortsel.reverse = mn_get_revsort (state->msgmap);
  5607.     sortsel.cursort = mn_get_sort (state->msgmap);
  5608.     sortsel.helptext = get_help_text (h_select_sort, NULL);
  5609.     sortsel.rval = 0;
  5610.  
  5611.     if ((retval = os_sortdialog (&sortsel))) {
  5612.         mn_set_revsort (state->msgmap, sortsel.reverse);
  5613.         mn_set_sort (state->msgmap, sortsel.cursort);
  5614.         }
  5615.  
  5616.     if (sortsel.helptext != NULL) 
  5617.         free_help_text (sortsel.helptext);
  5618.     return (retval);
  5619.     }
  5620. #endif
  5621.  
  5622.     /*----- String together the prompt ------*/
  5623.     tmp[1] = '\0';
  5624.     strcpy(prompt, "Choose type of sort, or Reverse current sort : ");
  5625.     for(i = 0; state->sort_types[i] != EndofList && i < 8; i++) {
  5626.     sorts[i].rval       = i;
  5627.     p = sorts[i].label = sort_name(state->sort_types[i]);
  5628.     while(*(p+1) && islower((unsigned char)*p))
  5629.       p++;
  5630.  
  5631.     sorts[i].ch   = tolower((unsigned char)(tmp[0] = *p));
  5632.     sorts[i].name = cpystr(tmp);
  5633.  
  5634.         if(mn_get_sort(state->msgmap) == state->sort_types[i])
  5635.       deefault = sorts[i].rval;
  5636.     }
  5637.  
  5638.     sorts[i].ch     = 'r';
  5639.     sorts[i].rval   = 'r';
  5640.     sorts[i].name   = cpystr("R");
  5641.     sorts[i].label  = "Reverse";
  5642.     sorts[++i].ch   = -1;
  5643.     help = h_select_sort;
  5644.  
  5645.     if((s = radio_buttons(prompt,ql,sorts,deefault,'x',help,RB_NORM)) != 'x'){
  5646.     state->mangled_body = 1;        /* signal screen's changed */
  5647.     if(s == 'r'){
  5648.         mn_set_revsort(state->msgmap, !mn_get_revsort(state->msgmap));
  5649.     }
  5650.     else
  5651.       mn_set_sort(state->msgmap, state->sort_types[s]);
  5652.     }
  5653.     else{
  5654.     retval = 0;
  5655.     cmd_cancelled("Sort");
  5656.     }
  5657.  
  5658.     while(--i >= 0)
  5659.       fs_give((void **)&sorts[i].name);
  5660.  
  5661.     blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
  5662.     return(retval);
  5663. }
  5664.  
  5665.  
  5666.  
  5667.  
  5668. /*----------------------------------------------------------------------
  5669.   Build comma delimited list of selected messages
  5670.  
  5671.   Args: stream -- mail stream to use for flag testing
  5672.     msgmap -- message number struct of to build selected messages in
  5673.     count -- pointer to place to write number of comma delimited
  5674.  
  5675.   Returns: malloc'd string containing sequence, else NULL if
  5676.        no messages in msgmap with local "selected" flag.
  5677.   ----*/
  5678. char *
  5679. selected_sequence(stream, msgmap, count)
  5680.     MAILSTREAM *stream;
  5681.     MSGNO_S    *msgmap;
  5682.     long       *count;
  5683. {
  5684.     long  i;
  5685.  
  5686.     if(!any_lflagged(msgmap, MN_SLCT) || !stream)
  5687.       return(NULL);
  5688.  
  5689.     /*
  5690.      * The plan here is to use the c-client elt's "sequence" bit
  5691.      * to work around any orderings or exclusions in pine's internal
  5692.      * mapping that might cause the sequence to be artificially
  5693.      * lengthy.  It's probably cheaper to run down the elt list
  5694.      * twice rather than call nm_raw2m() for each message as
  5695.      * we run down the elt list once...
  5696.      */
  5697.     for(i = 1L; i <= stream->nmsgs; i++)
  5698.       mail_elt(stream, i)->sequence = 0;
  5699.  
  5700.     for(i = 1L; i <= mn_get_total(msgmap); i++)
  5701.       if(get_lflag(stream, msgmap, i, MN_SLCT)){
  5702.       /*
  5703.        * Forget we knew about it, and set "add to sequence"
  5704.        * bit...
  5705.        */
  5706.       clear_index_cache_ent(i);
  5707.       mail_elt(stream, mn_m2raw(msgmap, i))->sequence = 1;
  5708.       }
  5709.  
  5710.     return(build_sequence(stream, NULL, count));
  5711. }
  5712.  
  5713.  
  5714. /*----------------------------------------------------------------------
  5715.   Build comma delimited list of current, flagged messages
  5716.  
  5717.   Args: stream -- mail stream to use for flag testing
  5718.     msgmap -- message number struct of to build selected messages in
  5719.     flag -- system flag to 
  5720.     count -- pointer to place to write number of comma delimited
  5721.     mark -- mark index cache entry changed, and count state change
  5722.  
  5723.   Returns: malloc'd string containing sequence, else NULL if
  5724.        no messages in msgmap with local "selected" flag (a flag
  5725.        of zero means all current msgs).
  5726.   ----*/
  5727. char *
  5728. currentf_sequence(stream, msgmap, flag, count, mark)
  5729.     MAILSTREAM *stream;
  5730.     MSGNO_S    *msgmap;
  5731.     long    flag;
  5732.     long       *count;
  5733.     int        mark;
  5734. {
  5735.     long      i;
  5736.     MESSAGECACHE *mc;
  5737.  
  5738.     FETCH_ALL_FLAGS(stream);            /* make sure all flags valid */
  5739.     for(i = 1L; i <= stream->nmsgs; i++)
  5740.       mail_elt(stream, i)->sequence = 0;    /* clear "sequence" bits */
  5741.  
  5742.     for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
  5743.     /* if not already set, go on... */
  5744.     mc = mail_elt(stream, mn_m2raw(msgmap, i));
  5745.     if((flag == F_DEL && !mc->deleted)
  5746.        || (flag == F_UNDEL && mc->deleted)
  5747.        || (flag == F_SEEN && !mc->seen)
  5748.        || (flag == F_UNSEEN && mc->seen)
  5749.        || (flag == F_ANS && !mc->answered)
  5750.        || (flag == F_UNANS && mc->answered)
  5751.        || (flag == F_FLAG && !mc->flagged)
  5752.        || (flag == F_UNFLAG && mc->flagged))
  5753.       continue;
  5754.  
  5755.     mc->sequence = 1;            /* set "sequence" flag */
  5756.     if(mark){
  5757.         clear_index_cache_ent(i);        /* force new index line */
  5758.         check_point_change();        /* count state change */
  5759.     }
  5760.     }
  5761.  
  5762.     return(build_sequence(stream, NULL, count));
  5763. }
  5764.  
  5765.  
  5766. /*----------------------------------------------------------------------
  5767.   Build comma delimited list of messages with elt "sequence" bit set
  5768.  
  5769.   Args: stream -- mail stream to use for flag testing
  5770.     msgmap -- struct containing sort to build sequence in
  5771.     count -- pointer to place to write number of comma delimited
  5772.          NOTE: if non-zero, it's a clue as to how many messages
  5773.                have the sequence bit lit.
  5774.  
  5775.   Returns: malloc'd string containing sequence, else NULL if
  5776.        no messages in msgmap with elt's "sequence" bit set
  5777.   ----*/
  5778. char *
  5779. build_sequence(stream, msgmap, count)
  5780.     MAILSTREAM *stream;
  5781.     MSGNO_S    *msgmap;
  5782.     long       *count;
  5783. {
  5784. #define    SEQ_INCREMENT    128
  5785.     long    n = 0L, i, x, lastn = 0L, runstart = 0L;
  5786.     size_t  size = SEQ_INCREMENT;
  5787.     char   *seq = NULL, *p;
  5788.  
  5789.     if(count){
  5790.     if(*count > 0L)
  5791.       size = min((*count) * 4, 16384);
  5792.  
  5793.     *count = 0L;
  5794.     }
  5795.  
  5796.     for(x = 1L; x <= stream->nmsgs; x++){
  5797.     if(msgmap){
  5798.         if((i = mn_m2raw(msgmap, x)) == 0L)
  5799.           break;
  5800.     }
  5801.     else
  5802.       i = x;
  5803.  
  5804.     if(mail_elt(stream, i)->sequence){
  5805.         n++;
  5806.         if(!seq)                /* initialize if needed */
  5807.           seq = p = fs_get(size);
  5808.  
  5809.         /*
  5810.          * This code will coalesce the ascending runs of
  5811.          * sequence numbers, but fails to break sequences
  5812.          * into a reasonably sensible length for imapd's to
  5813.          * swallow (reasonable addtition to c-client?)...
  5814.          */
  5815.         if(lastn){                /* if may be in a run */
  5816.         if(lastn + 1L == i){        /* and its the next raw num */
  5817.             lastn = i;            /* skip writing anything... */
  5818.             continue;
  5819.         }
  5820.         else if(runstart != lastn){
  5821.             *p++ = (runstart + 1L == lastn) ? ',' : ':';
  5822.             sstrcpy(&p, long2string(lastn));
  5823.         }                /* wrote end of run */
  5824.         }
  5825.  
  5826.         runstart = lastn = i;        /* remember last raw num */
  5827.  
  5828.         if(n > 1L)                /* !first num, write delim */
  5829.           *p++ = ',';
  5830.  
  5831.         if(size - (p - seq) < 16){    /* room for two more nums? */
  5832.         size_t offset = p - seq;    /* grow the sequence array */
  5833.         size += SEQ_INCREMENT;
  5834.         fs_resize((void **)&seq, size);
  5835.         p = seq + offset;
  5836.         }
  5837.  
  5838.         sstrcpy(&p, long2string(i));    /* write raw number */
  5839.     }
  5840.     }
  5841.  
  5842.     if(lastn && runstart != lastn){        /* were in a run? */
  5843.     *p++ = (runstart + 1L == lastn) ? ',' : ':';
  5844.     sstrcpy(&p, long2string(lastn));    /* write the trailing num */
  5845.     }
  5846.  
  5847.     if(seq)                    /* if sequence, tie it off */
  5848.       *p  = '\0';
  5849.  
  5850.     if(count)
  5851.       *count = n;
  5852.  
  5853.     return(seq);
  5854. }
  5855.  
  5856.  
  5857.  
  5858. /*----------------------------------------------------------------------
  5859.   If any messages flagged "selected", fake the "currently selected" array
  5860.  
  5861.   Args: map -- message number struct of to build selected messages in
  5862.  
  5863.   OK folks, here's the tradeoff: either all the functions have to
  5864.   know if the user want's to deal with the "current" hilited message
  5865.   or the list of currently "selected" messages, *or* we just
  5866.   wrap the call to these functions with some glue that tweeks
  5867.   what these functions see as the "current" message list, and let them
  5868.   do their thing.
  5869.   ----*/
  5870. int
  5871. pseudo_selected(map)
  5872.     MSGNO_S *map;
  5873. {
  5874.     long i, later = 0L;
  5875.  
  5876.     if(any_lflagged(map, MN_SLCT)){
  5877.     map->hilited = mn_m2raw(map, mn_get_cur(map));
  5878.  
  5879.     for(i = 1L; i <= mn_get_total(map); i++)
  5880.       /* BUG: using the global mail_stream is kind of bogus since
  5881.        * everybody that calls us get's a pine stuct passed it.
  5882.        * perhaps a stream pointer in the message struct makes 
  5883.        * sense?
  5884.        */
  5885.       if(get_lflag(ps_global->mail_stream, map, i, MN_SLCT)){
  5886.           if(!later++){
  5887.           mn_set_cur(map, i);
  5888.           }
  5889.           else{
  5890.           mn_add_cur(map, i);
  5891.           }
  5892.       }
  5893.  
  5894.     return(1);
  5895.     }
  5896.  
  5897.     return(0);
  5898. }
  5899.  
  5900.  
  5901. /*----------------------------------------------------------------------
  5902.   Antidote for the monkey business committed above
  5903.  
  5904.   Args: map -- message number struct of to build selected messages in
  5905.  
  5906.   ----*/
  5907. void
  5908. restore_selected(map)
  5909.     MSGNO_S *map;
  5910. {
  5911.     if(map->hilited){
  5912.     mn_reset_cur(map, mn_raw2m(map, map->hilited));
  5913.     map->hilited = 0L;
  5914.     }
  5915. }
  5916.  
  5917.  
  5918. /*
  5919.  * Get the user name from the mailbox portion of an address.
  5920.  *
  5921.  * Args: mailbox -- the mailbox portion of an address (lhs of address)
  5922.  *       target  -- a buffer to put the result in
  5923.  *       len     -- length of the target buffer
  5924.  *
  5925.  * Returns the left most portion up to the first '%', ':' or '@',
  5926.  * and to the right of any '!' (as if c-client would give us such a mailbox).
  5927.  * Returns NULL if it can't find a username to point to.
  5928.  */
  5929. char *
  5930. get_uname(mailbox, target, len)
  5931.     char  *mailbox,
  5932.       *target;
  5933.     int    len;
  5934. {
  5935.     int i, start, end;
  5936.  
  5937.     if(!mailbox || !*mailbox)
  5938.       return(NULL);
  5939.  
  5940.     end = strlen(mailbox) - 1;
  5941.     for(start = end; start > -1 && mailbox[start] != '!'; start--)
  5942.         if(strindex("%:@", mailbox[start]))
  5943.         end = start - 1;
  5944.  
  5945.     start++;            /* compensate for either case above */
  5946.  
  5947.     for(i = start; i <= end && (i-start) < (len-1); i++) /* copy name */
  5948.       target[i-start] = isupper((unsigned char)mailbox[i])
  5949.                       ? tolower((unsigned char)mailbox[i])
  5950.                       : mailbox[i];
  5951.  
  5952.     target[i-start] = '\0';    /* tie it off */
  5953.  
  5954.     return(*target ? target : NULL);
  5955. }
  5956.  
  5957.  
  5958. /*
  5959.  * file_lister - call pico library's file lister
  5960.  */
  5961. int
  5962. file_lister(title, path, file, newmail, flags)
  5963.     char *title, *path, *file;
  5964.     int   newmail, flags;
  5965. {
  5966.     PICO pbuf;
  5967.     int  rv;
  5968.  
  5969.     memset(&pbuf, 0, sizeof(PICO));
  5970. /* BUG: what about help command and text? */
  5971.     pbuf.raw_io        = Raw;
  5972.     pbuf.showmsg       = display_message_for_pico;
  5973.     pbuf.keybinit      = init_keyboard;
  5974.     pbuf.helper        = helper;
  5975.     pbuf.resize           = resize_for_pico;
  5976.     pbuf.browse_help   = h_composer_browse;
  5977.     pbuf.menu_rows     = FOOTER_ROWS(ps_global) - 1;
  5978.     pbuf.pine_anchor   = title;
  5979.     pbuf.pine_version  = pine_version;
  5980.     pbuf.pine_flags    = flags_for_pico(ps_global);
  5981.     if(ps_global->VAR_OPER_DIR){
  5982.     pbuf.oper_dir    = ps_global->VAR_OPER_DIR;
  5983.     pbuf.pine_flags |= P_TREE;
  5984.     }
  5985.  
  5986.     if(newmail)
  5987.       pbuf.newmail = new_mail_for_pico;
  5988.  
  5989.     rv = pico_file_browse(&pbuf, path, file, NULL, flags);
  5990.     fix_windsize(ps_global);
  5991.     init_signals();        /* has it's own signal stuff */
  5992.     redraw_titlebar();
  5993.     if(ps_global->redrawer != (void (*)())NULL)
  5994.       (*ps_global->redrawer)();
  5995.  
  5996.     redraw_keymenu();
  5997.     return(rv);
  5998. }
  5999.